quinta-feira, fevereiro 28, 2008

FreeBSD 7.0!

Já foi! Lançaram ontem (me atrasei a postar no blog).

Já vou baixar e deixar o CD gravado e guardadinho pras horas certas :) E vou também atualizar o servidor da RG3 que tá rodando o 7.0-RC2 ainda. Ôh, notícia boa!!!

Que o download comece!

http://www.freebsd.org/releases/7.0R/announce.html

----- texto adicionado -----

Essa não podia deixar de postar. Tenho que adicionar essa imagem a este post. Fui baixar a ISO como tinha dito acima, e vejam só: rsrs



Tá todo mundo baixando. Na lista da FUG tá uma euforia só!

segunda-feira, fevereiro 18, 2008

Flash Player para Linux 64-bits (amd64)

Muitos vão acabar chegando aqui pelo Google, achando que encontraram a solução para o Flash Player em Linux amd64. Bem, a solução completa não tenho aqui (posso até fazer um post explicando como fiz o Flash funcionar no meu Debian amd64), mas tenho a raiz da solução do problema.

Gostaria de pedir a vocês que visitassem duas páginas:

http://www.petitiononline.com/lin64swf/petition.html
http://www.adobe.com/cfusion/mmform/index.cfm?name=wishform

A primeira, é uma petição on-line para que os desenvolvedores da Macromedia façam uma versão de 64-bits do Flash Player pra Linux. A segunda, é uma página do próprio site da Macromedia onde é possível pedir novos recursos, no caso, devemos solicitar uma versão de 64-bits do Flash Player pra Linux (foi o que eu fiz).

A expectativa é que através da petição e dos pedidos pelo site da Macromedia, eles finalmente façam uma versão de 64-bits pra Linux. Isso resolveria nossos problemas, pois não precisaríamos mais manter versões de 32-btis dos nossos navegadores pra poder ver conteúdo em Flash.

Eu por exemplo, tenho um notebook AMD64, mas preciso usar o Firefox de 32-bits por causa do Flash. Isso me deixa irritado! :P

Pois bem, vamos assinar a petição e encher o saco da Macromedia até que eles façam a bendita versão de 64-bits.

segunda-feira, fevereiro 11, 2008

RG3.Net de cara nova

Neste último domingo, dia 10 de fevereiro, terminei a aplicação de redirecionamento da RG3.Net e fiz o upload pro servidor. Instalei tudo em uma máquina recém-formatada com FreeBSD 7.0 RC1.

O que mudou?

Absolutamente tudo!

A página principal agora tem também textos sobre o provedor de Internet a Rádio que temos aqui em Feira de Santana e uns links pra testadores de velocidade.

O site do provedor está com mais detalhes sobre o serviço e os planos de acesso e também um pequeno "how-to" explicando ao cliente como criar sua conexão com a Internet.

Mas a principal mudança foi no serviço de redirecionamento. O sistema antigo era feito em PHP e tinha algumas falhas de programação. Não vou falar muito sobre elas pra não ofender o programador antigo, que eu nem sei quem é (compramos a RG3 já pronta, não fomos nós que fundamos).

O sistema novo foi desenvolvido completamente do zero em Perl com Catalyst. Com bem menos linhas de código, escrevi uma nova aplicação com recursos a mais (mais de um domínio para escolha além de três formas de recuperação de senha) e interface gráfica melhorada.

No sistema antigo, quando o usuário fazia logon, os campos de login e senha que ficavam no menu esquerdo do site eram substituídos por três links que davam acesso às opções dos redirecionamentos. Isso era ruim, pois o usuário via a mesma página, com apenas um pequeno detalhe diferente e a maioria acreditava que não estava conseguindo fazer login, já que a página que aparecia após o "Enter" era praticamente a mesma. O que isto causava? Vários e-mails do tipo "Não consigo entrar na minha conta!!!".

O sistema novo roda também no Apache, como o antigo, porém, agora com FastCGI. Pra quem não conhece, o FastCGI permite que uma aplicação Web, no meu caso, uma aplicação em Catalyst, faça um melhor gerenciamento das requisições. Coisas comuns à aplicação, como acesso ao servidor de banco de dados, por exemplo, acontece apenas uma vez, melhorando o desempenho. No sistema em PHP, cada vez que um redirecionamento era solicitado, o programa abria uma conexão com o banco, pegava os dados e fechava a conexão. Com Catalyst no FastCGI, a conexão é aberta apenas uma vez, quando a aplicação inicia. Sempre que um redirecionamento é solicitado, a aplicação usa a conexão já aberta pra obter os dados. Diferentemente do CGI puro, onde cada requisição criava um novo processo no servidor, com FastCGI apenas um processo é criado. Em alguns momentos, quando ocorrem muitas requisições, alguns processos a mais são criados, mas ainda assim, cada processo existente gerencia várias requisições, poupando memória e recursos.

Leiam mais sobre a performance do FastCGI em http://www.fastcgi.com/devkit/doc/fcgi-perf.htm

Além dessas alterações internas no sistema e mudança de interface gráfica, quatro novos domínios foram adicionados, possibilitando outras alternativas de redirecionamentos:


  • irg3.net

  • minhapagina.info

  • minhahome.com

  • minhahomepage.com



A idéia é sempre adicionar novos domínios (essa tarefa ficou bem fácil na aplicação nova :D) dando mais opções pros usuários.

Já ia esquecendo, no sistema novo, apenas um login e o e-mail são solicitados na hora do cadastro. Nem o nome a gente pergunta mais. Pra que? Geralmente não colocam o nome verdadeiro mesmo :P E pensar que no sistema antigo pedia até CPF... putz!

quinta-feira, fevereiro 07, 2008

Do Red Hat para o Slackware para o Debian

Nostalgia! De vez em quando gosto de relembrar o passado e uma parte do passado do meu eterno percurso na computação que mais gosto é quando comecei a mexer com Linux (e por tabela, FreeBSD).

Tudo começou quando entrei pro curso de Física da UEFS. Peguei um trabalho voluntário pra desenvolver a home page do departamento e me botaram pra trabalhar numa estação com Red Hat 7.0. As minhas únicas tentativas com Linux até o momento foram o Corel Linux, um Linux da Corel que não foi pra frente, e o Monkey Linux, que era um mini-linux que rodava em partições FAT32 (é, isso existia!). O Monkey Linux foi minha melhor experiência com mini-linux, pois rodava tudo nele, menos o X (que na época era o XFree86), mas depois eu descobri que o problema era que o XFree86 não tinha drivers pra minha placa de vídeo da época (SiS 6202).

Com o Red Hat foi que comecei a usar Linux de verdade. Três professores (Dagoberto, Jorge Kaschny e Nazareno) me ensinavam os básicos do sistema. Kaschny era o que mais fuçava na época. Aprendi muito com eles três e mais ainda com um colega de curso, Alex Gamas, que me deu muitas dicas na época e que junto com o irmão (Leonardo Gamas), aproximadamente um ano depois, me colocaram no mundo do FreeBSD.

Os irmãos Gamas me mostraram em seguida o Slackware. Eles me conseguiram um CD do Slackware 7.0 e eu instalei o sistema em casa. Nessa época, o Windows 98 já tinha se mandado de uma vez, dando espaço pro Red Hat. Quando comecei a usar o Slackware, delirei! Adotei como o meu Linux preferido e o utilizei por muito tempo: de 2002 até 2005. Fui até o Slackware 10.

Em 2006, comecei a usar o Debian, por pressão dos amigos de Ilhéus, quando ainda cursava Ciência da Computação na UESC. O que me levou a adotar o Debian foi o tamanho do seu repositório de pacotes pré-compilados. Eu já tinha enjoado de ter que compilar tudo pra rodar no Slackware.

Fiquei usando o Debian e continuo com ele até hoje. Acredito que seja a melhor distribuição Linux existente, pois mistura a leveza e simplicidade do Slackware com a facilidade de uso do Red Hat. Não é tão seco nem tão pesado, foi o meio-termo que encontrei no mundo do Linux. Claro que pra iniciantes e leigos, o Ubuntu seria uma melhor opção, mas aí você já sai do meio-termo e tende mais para o "pesado" do Red Hat e Fedora. :P

sexta-feira, novembro 23, 2007

Em breve: wxWidgets 3.0

Olá, pessoal! Quanto tempo, hein!? :P

Estive fora desde julho por falta de tempo. Vejam só:


  • Em março, tomei a frente de um provedor de Internet à Rádio em Feira de Santana, BA - RG3.Net. Além de ter que corrigir todos os erros de rede (tanto nos servidores quanto na estrutura wireless), gastei um bom tempo desenvolvendo uma aplicação Web (Perl com Catalyst) pra gerenciar o provedor (que estava meio bagunçado, tudo era feito no papel). Apenas nesse mês de novembro consegui um pouco mais de paz, pois corrigi os últimos problemas de hardware dessa rede wireless (o próximo passo é otimizá-la mais ainda).


  • Estou no último semestre da Universidade, o que significa que tenho que desenvolver algo na disciplina "Estágio Supervisionado". A sorte é que usei a própria RG3.Net como meu tema de estágio, então o que faço lá tenho que escrever em um relatório e apresentar agora em dezembro.


  • O site da RG3.Net também está sendo mudado (por dentro), como também os servidores. Dois deles já foram formatados e reinstalados. Lá rodava (e ainda roda) FreeBSD.



Todo esse trabalho na RG3.Net me ocupou bastante, até o WinPolicy ficou sem atualizações. Mas as coisas estão voltando ao normal. Com o fim do curso de Ciência da Computação, vou poder configurar melhor meu tempo e o blog terá seu espaço de volta :)

Na verdade, o que eu queria dizer é que o wxWidgets 3.0 está prestes a sair. Sua principal mudança vai ser que agora, UNICODE e ANSI estarão juntos em um único build da biblioteca. Algumas classes foram melhoradas e bugs corrigidos.

Mais detalhes no wxBlog.

quinta-feira, julho 12, 2007

traceroute.org e aniversário do blog

Um site interessante.

Descobri ele quando estava tendo problemas de roteamento em um dos meus servidores. Pacotes que saiam de um IP específico da minha rede não saiam do país. Além disso, ninguém fora do país conseguia enviar pacotes para esse IP. Mas outros IPs da mesma rede funcionavam OK (no final das contas, foi um filtro na Embratel que estava causando o problema).

Então, comecei a testar as rotas para esse meu IP. Primeiro, fiz um traceroute a partir de um dos servidores da Dreamhost. No traceroute, percebi que no meio do caminho, o pacote era bloqueado. Eu queria fazer mais testes, vindos de outras máquinas no exterior, mas o servidor da Dreamhost era a única máquina com acesso remoto que eu podia entrar.

Nesas buscas pelo Google, encontrei o site www.traceroute.org. A partir deste site, você pode acessar outros sites em várias partes do mundo que permitem que você faça traceroutes para qualquer IP. Com ele, pude fazer testes de traceroute a partir de máquinas em vários outros países.

Muito legal este site. Recomendo aos administradores de redes. :)

PS: Comemoremos! Hoje faz exatamente 1 ano da Casa de Just! :D

segunda-feira, julho 09, 2007

numeros.pl

Estava eu nesse fim de domingo, em casa, sem ter o que fazer. Nesse momento de ócio, lembrei de um numerólogo que estava dando entrevista essa semana na TV e procurei por Numerologia na Wikipédia. Comecei a ler o artigo e decidi fazer uns testes.

O problema é que é chato ficar somando os valores das letras dos nomes pra saber o número da pessoa. Então, como bom cientista da computação que sou, implementei um programinha pra fazer essa tarefa.

Pois bem, escrevi o código em Perl. Como programas em Perl são interpretados e não compilados, você vai precisar ter o interpretador instalado em seu computador pra executar o programa. Quem usa Windows, pode tentar o ActivePerl. Quem usa Linux ou outro UNIX, provavelmente já tem o Perl instalado (se não tiver, você pode baixar o fonte e compilar ou instalar o pacote correspondente). Pra executar o programa, use a linha de comando:

perl numeros.pl


O programa irá perguntar seu nome completo e sua data de nascimento. Em seguida, vai somar os valores de cada letra do nome e dar seu número na numerologia. Depois, vai somar os números da sua data de nascimento e lhe entregar o número correspondente à sua lição de vida. Aí é só usar o artigo da Wikipédia pra saber o que cada número significa.

#!/usr/bin/perl
# Referencia: http://pt.wikipedia.org/wiki/Numerologia

# Soma os digitos
sub soma {
my $numero = 0;
my (@digitos) = split(//, $_[0]);

for (my $i = 0; $i < @digitos; ++$i) {
$numero += abs($digitos[$i]);
}

if ($numero >= 10) {
return &soma($numero);
} else {
return $numero;
}
}

# Tabela de numerologia
my $tabela = ({
a => 1, b => 2, c => 3, d => 4, e => 5, f => 6,
g => 7, h => 8, i => 9, j => 1, k => 2, l => 3,
m => 4, n => 5, o => 6, p => 7, q => 8, r => 9,
s => 1, t => 2, u => 3, v => 4, w => 5, x => 6,
y => 7, z => 8,
});

# Solicita o nome
my $nome = undef;
do {
print "Digite seu nome completo, sem acentos ou cedilha:\n";
$nome = lc(<STDIN>);

chop($nome);
} while ($nome eq '');

# Solicida data de nascimento
my $nasc = undef;
do {
print "Digite sua data de nascimento, ex.: 10\/11\/1982\n";
$nasc = lc(<STDIN>);

chop($nasc);
} while ($nasc eq '');


# Divide as letras e calcula a soma
my (@letras) = split(//, $nome);
my $numero = 0;

for (my $i = 0; $i < @letras; ++$i) {
next if $letras[$i] eq ' ';
$numero += $tabela->{$letras[$i]};
}

# Exibe o resultado
print "Seu numero: " . &soma($numero) . "\n";
print "Sua licao de vida: " . &soma($nasc) . "\n";

sábado, julho 07, 2007

Novo: Slackware 12

Isso aqui vai acabar virando blog de notícias de atualização de distros :P Mas pelo menos tô voltando à ativa, rsrs

Pois bem, o Slackware 12 veio essa semana e algumas mudanças são notáveis. De acordo com o anúncio oficial, o Slackware agora vem com dois tipos de kernels: kernels grandes, que vêm com todo tipo de driver possível, geralmente pra facilitar a instalação do sistema, além do kernel genérico, que tem os drivers compilados como módulo (a forma mais comum de kernel).

Outra novidade é que ele agora vem com o HAL (Hardware Abstraction Layer), aumentando a habilidade Plug and Play do Slackware.

A nova versão atualiza também a maioria dos pacotes: kernel 2.6.21.5, KDE 3.5.7, X.Org 7.2.0, gcc 4.1.2, Apache 2.2.4, etc.

Para a lista completa das mudanças, leiam o anúncio oficial.

domingo, abril 08, 2007

Debian 4.0 (etch) se torna stable!

Finalmente! Aguardava esse momento há alguns meses.

O Debian 4.0, codenome etch, deixou de ser testing e agora é stable. Ele havia sido congelado em dezembro do ano passado, ou seja, novos pacotes deixaram de ser adicionados e os pacotes já contidos no sistema estavam em fase de correção de bugs.

A notícia oficial pode ser lida no site do Debian:

http://www.debian.org/News/2007/20070408

Como também, instruções de download e instalação:

http://www.debian.org/distrib/

Quem usa o Justsoft GNU/Linux, já está rodando o sistema Justsoft em cima do Debian etch. Portanto, basta atualizar o sistema através do APT ou do próprio assistente de atualização que fica ao lado do relógio do GNOME.

Apesar de agora ser stable, vou esperar um pouco antes de atualizar meu servidor, que já usa Debian 3.1 (sarge).

domingo, março 18, 2007

Curso de wxWidgets, post 14: Comunicando-se pela rede

Olá, pessoal! Como havia prometido no post anterior, este será sobre sockets e comunicação via rede.

Nesta primeira parte, abordarei o cliente, ou seja, o programa irá conectar em algum servidor. Tive a idéia de fazer um código para enviar e-mails, já que o protocolo SMTP é simples e fácil para mensagens em texto plano e sem anexos. Para quem não conhece o protocolo SMTP, sugiro que leia a RFC822, que descreve seu formato:

ftp://ftp.rfc-editor.org/in-notes/rfc822.txt

De qualquer forma, vou dar uma resumida no protocolo.

Nos parágrafos abaixo, <- indica uma mensagem que está vindo do servidor e -> indica uma mensagem que o cliente está enviando para o servidor.

Ao se conectar em um servidor SMTP, o cliente recebe uma mensagem de boas-vindas, geralmente com o endereço do servidor e alguns dados do software:

<- 220 spaceymail-mx1.g.dreamhost.com ESMTP


Em seguida, o cliente deve se idenficar enviando a string "EHLO" e o seu endereço. Após a identificação, o servidor dá algumas informações sobre o envio da mensagem (não relevante para nós no momento):

-> EHLO just.justsoft.com.br
<- 250-spaceymail-mx1.g.dreamhost.com
<- 250-PIPELINING
<- 250-SIZE 40960000
<- 250-ETRN
<- 250-STARTTLS
<- 250 8BITMIME


Agora o cliente deve dizer o endereço do remetente com o comando "MAIL From:" e do destinatário com "RCPT To:":

-> MAIL From: <teste@teste.com>
<- 250 Ok
-> RCPT To: <jpjust@justsoft.com.br>
<- 250 Ok


Após estas informações, o cliente começa a mensagem com o comando "DATA":

-> DATA
<- 354 End data with <CR><LF>.<CR><LF>
-> From: <just@just.com>
-> To: <jpjust@justsoft.com.br>
-> Date, Thu, 15 Mar 2007 21:00:00 +0000
-> Subject: Teste
->
-> teste
->
-> .
<- 250 Ok: queued as 6C0C3CE937


O fim da mensagem deve ser indicado com um ponto (.) sozinho em uma linha. Por último, o cliente pede para o servidor fechar a conexão com "QUIT":

-> QUIT
<- 221 Bye


E é isto! Você pode testar os comandos conectando em algum servidor SMTP pelo telnet. Basta executar o comando "telnet mx1.hotmail.com 25" para conectar no servidor SMTP do Hotmail e enviar mensagens para endereços de lá. Veja abaixo uma pequena lista com o servidor SMTP de alguns serviços de e-mail famosos:

Hotmail: mx1.hotmail.com
Yahoo!: a.mx.mail.yahoo.com
GMail: gmail-smtp-in.l.google.com
UOL: mx.uol.com.br
BOL: mx.boil.com.br


Se você usa Linux ou algum outro UNIX, use o comando host para saber o servidor SMTP de algum domínio de e-mail. Por exemplo, para saber o servidor SMTP de gmail.com:

$ host -t MX gmail.com


Voltando à programação, o que iremos fazer é um programa em wxWidgets para se conectar a um servidor SMTP e enviar um e-mail.

A parte wxWidgets da coisa

Para todo o trabalho de rede no wxWidgets, usaremos a classe wxSocketBase e uma classe derivada, a wxSocketClient.

wxSocketBase é a classe base para todas as outras classes de socket no wxWidgets. Neste post, usaremos apenas a classe wxSocketClient, que é responsável por fazer as conexões do nosso programa, que é o cliente, no servidor.

Conectando-se no servidor

Antes de fazer alguma conexão com a wxSocketClient, precisamos do endereço do servidor ao qual vamos nos conectar e também a porta da conexão. Para manusear essas informações, usaremos a classe wxIPV4address. Essa classe serve para guardar informações de um endereço, como número IP e porta. Para definir o endereço do servidor e a porta, usamos dois métodos:

wxIPV4address host;
host.Hostname(wxT("smtp.server.com"));
host.Service(wxT("25"));


No trecho de código acima, criamos um objeto de nome host, que é uma instância de wxIPV4address. Em seguida, definimos o endereço como smtp.server.com e a porta como 25.

Após criar um objeto com o endereço e a porta do servidor, podemos chamar o método wxSocketClient::Connect() para conectar no servidor:

wxSocketClient sock;
bool status;
status = sock.Connect(host);


Se a conexão for feita com sucesso, o método retorna true, que será guardado na variável status. Em caso de erro na conexão, logicamente, o método retornará false.

Por padrão, o método wxSocketClient::Connect() aguarda a conexão ser feita ou a ocorrência de um erro para prosseguir a execução. Mas é possível chamar o método e continuar a execução do programa enquanto o socket está se conectando, basta adicionar um parâmetro ao método:

sock.Connect(host, false);


O segundo parâmetro indica se o método deverá aguardar a conexão ser completada. Se você escolheu esta maneira para se conectar, poderá verificar se a conexão foi feita posteriormente com o método wxSocketBase::IsConnected() ou até mesmo, aguardar pela conexão em um ponto posterior com wxSocketClient::WaitOnConnect().

Enviando dados

Após ter sido feita a conexão, já é possível enviar e receber dados. Para fazer o envio, usamos o método wxSocketBase::Write(). Após enviar qualquer dado, podemos verificar se o envio foi feito com sucesso com o método wxSocketBase::Error():

wxString dado = wxT("EHLO localhost\r\n");
socket.Write((char *)dado.mb_str(), dado.Len());
if (socket.Error())
{
wxMessageBox(wxT("Erro ao enviar o dado."));
}


No trecho acima, enviamos a string "EHLO localhost\r\n" e em caso de erro, uma mensagem é exibida ao usuário. Perceba o cast (char *) e o método wxString::mb_str(). Este método retorna a string no formato ANSI. Perceba também que o método wxSocketBase::Write() não define um tipo de dado específico, você pode enviar texto puro ou binário. Como no nosso exemplo de um cliente SMTP estamos enviando texto puro, obtemos o formato ANSI da string e usamos o cast (char *) para indicar ao método que estamos enviando um char. O segundo parâmetro do método indica o tamanho do dado que estamos enviando. No caso, como é texto puro em ANSI (1 byte por caracter), indicamos o tamanho da string.

Em caso de erro no envio, wxSocketBase::Error() retorna true.

Recebendo dados

Após enviar algum dado pelo socket, geralmente esperamos por uma resposta, esta é a hora de fazer a leitura do socket. Para isto, usaremos o método wxSocketBase::Read():

char buffer[1024] = {0};
socket.Read(buffer, 1024 - 1);
if (socket.Error())
{
wxMessageBox(wxT("Erro ao fazer a leitura."));
}
int contagem = socket.LastCount();


Primeiro, criamos um buffer do tipo char com 1 KB. Em seguida, fazemos a leitura, armazenando a saída em buffer e indicando o máximo de dados que deverá ser lido (subtraímos 1 do tamanho pois devemos guardar um espaço para o terminador de string \0 da variável).

Mais uma vez, wxSocketBase::Error() entra em ação para nos informar se houve algum erro.

Neste trecho, usamos também o método wxSocketBase::LastCount(), ele retorna o número de bytes lidos no wxSocketBase::Read(). Ele também pode ser usado após um wxSocketBase::Write() para saber quantos bytes foram enviados de fato.

Fechando a conexão

Após enviarmos e recebermos todos os dados necessários para a conexão, devemos terminá-la. Basta um único método para isso, o wxSocketBase::Close():

socket.Close()


Existe também um outro método relacionado ao término de conexão, o wxSocketBase::WaitForLost(). Com ele, você pode indicar um timeout em segundos ou milissegundos. Se a conexão for fechada antes do timeout (o servidor pode fechar a conexão), o método retorna true, caso o timeout seja atingido, ele retorna false.

Outras formas de E/S

Além do wxSocketBase::Read() e do wxSocketBase::Write(), existem também o wxSocketBase::WriteMsg() e o wxSocketBase::ReadMsg(). Com esses dois métodos, é possível trocar mensagens entre duas aplicações em wxWidgets sem precisar se preocupar com a contagem de bytes enviados ou recebidos.

Mostrarei estes e outros métodos das classes de socket em posts futuros. Ainda há muito o que falar sobre sockets no wxWidgets :)

Configurando o socket

Também é possível fazer algumas configurações no socket antes de utilizá-lo. Veja abaixo.

wxSocketBase::SetTimeout(): configura o tempo de timeout em segundos para os métodos de E/S e de espera do socket. O valor padrão é de 10 minutos.

socket.SetTimeout(120); // Configura o timeout para 120 segundos


wxSocketBase::SetFlags(): configura o comportamento de espera do socket nas operações de E/S. Pode receber como argumento os valores abaixo:

wxSOCKET_NONE: Funcionamento normal.
wxSOCKET_NOWAIT: Lê ou grava o máximo possível de dados e retorna imediatamente.
wxSOCKET_WAITALL: Aguarda que todo o dado seja lido ou gravado ou que um erro ocorra para retornar.
wxSOCKET_BLOCK: Bloqueia a interface gráfica durante a operação.
wxSOCKET_REUSEADDR: Permite que o socket escute em uma porta que já está em uso (apenas para sockets de servidor).

Para configurar o socket para sempre aguardar que os dados sejam lidos ou gravados por completo antes de continuar a execução do código, use:

socket.SetFlags(wxSOCKET_WAITALL);


Outros métodos de configuração do socket serão vistos nos outros posts.

Nosso código de exemplo

Finalmente, o código do programa que irá fazer a conexão e enviar o e-mail. O código está bem documentado para que você possa entender cada parte do programa.

A interface gráfica já vai aparecer preenchida com os dados do servidor SMTP do GMail e meu endereço de e-mail de lá. Por favor, envie um e-mail para mim pelo programa de exemplo :) Obrigado.

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Enviando e-mails via SMTP
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just <jpjust@justsoft.com.br>
* A Casa de Just - http://jpjust.blogspot.com
* 18 de março de 2007, 00:48, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/socket.h>
#include <string.h>
#include <time.h>

// Tamanho do buffer que será utilizado no recebimento de mensagens
#define BUFFER 256

// Enumeração dos IDs
enum
{
ID_ENVIAR
};

// Classe: MailApp
class MailApp: public wxApp
{
public:
virtual bool OnInit();
};

// Classe: MailFrame
class MailFrame: public wxFrame
{
public:
MailFrame(void);

DECLARE_EVENT_TABLE()

private:
void EnviarMensagem(wxCommandEvent &event); // Método para enviar a mensagem
wxString Envia(wxSocketBase *socket, wxString msg); // Método para enviar dados pelo socket
wxString Le(wxSocketBase *socket); // Método para obter dados no socket

wxStaticText *lb_servidor;
wxStaticText *lb_porta;
wxStaticText *lb_de;
wxStaticText *lb_para;
wxStaticText *lb_assunto;

wxTextCtrl *txt_servidor;
wxTextCtrl *txt_porta;
wxTextCtrl *txt_de;
wxTextCtrl *txt_para;
wxTextCtrl *txt_assunto;
wxTextCtrl *txt_mensagem;
wxTextCtrl *txt_proto;

wxButton *btn_enviar;
};

// Tabela de eventos
BEGIN_EVENT_TABLE(MailFrame, wxFrame)
EVT_BUTTON(ID_ENVIAR, MailFrame::EnviarMensagem)
END_EVENT_TABLE()

// Método: MailApp::OnInit()
// Inicialização do programa
bool MailApp::OnInit()
{
MailFrame *frame = new MailFrame();
frame->Show();
return true;
}

// Método: MailFrame::MailFrame
// Construtor do frame
MailFrame::MailFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Enviar e-mail - http://jpjust.blogspot.com"))
{
//wxMessageBox(wxNow());

// Sizers
wxGridSizer *sizer_g = new wxGridSizer(5, 2, 0, 0);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

// Texto indicativo
lb_servidor = new wxStaticText(this, wxID_ANY, wxT("Servidor SMTP:"));
lb_porta = new wxStaticText(this, wxID_ANY, wxT("Porta:"));
lb_de = new wxStaticText(this, wxID_ANY, wxT("De:"));
lb_para = new wxStaticText(this, wxID_ANY, wxT("Para:"));
lb_assunto = new wxStaticText(this, wxID_ANY, wxT("Assunto:"));

// Caixas de texto
txt_servidor = new wxTextCtrl(this, wxID_ANY, wxT("gmail-smtp-in.l.google.com"), wxDefaultPosition, wxSize(200, -1));
txt_porta = new wxTextCtrl(this, wxID_ANY, wxT("25"));
txt_de = new wxTextCtrl(this, wxID_ANY, wxT("seu@email.com"), wxDefaultPosition, wxSize(200, -1));
txt_para = new wxTextCtrl(this, wxID_ANY, wxT("just1982@gmail.com"), wxDefaultPosition, wxSize(200, -1));
txt_assunto = new wxTextCtrl(this, wxID_ANY, wxT("Post 14"), wxDefaultPosition, wxSize(200, -1));
txt_mensagem = new wxTextCtrl(this, wxID_ANY, wxT("Eu li o post 14!"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_WORDWRAP);
txt_proto = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_WORDWRAP | wxTE_READONLY);

// Botão de enviar
btn_enviar = new wxButton(this, ID_ENVIAR, wxT("Enviar"));

// Adiciona itens nos sizers
sizer_g->Add(lb_servidor, 0, wxALL, 5);
sizer_g->Add(txt_servidor, 0, wxALL, 5);
sizer_g->Add(lb_porta, 0, wxALL, 5);
sizer_g->Add(txt_porta, 0, wxALL, 5);
sizer_g->Add(lb_de, 0, wxALL, 5);
sizer_g->Add(txt_de, 0, wxALL, 5);
sizer_g->Add(lb_para, 0, wxALL, 5);
sizer_g->Add(txt_para, 0, wxALL, 5);
sizer_g->Add(lb_assunto, 0, wxALL, 5);
sizer_g->Add(txt_assunto, 0, wxALL, 5);

sizer_v->Add(sizer_g, 0, wxALL, 0);
sizer_v->Add(txt_mensagem, 2, wxALL | wxEXPAND, 5);
sizer_v->Add(txt_proto, 1, wxALL | wxEXPAND, 5);
sizer_v->Add(btn_enviar, 0, wxALL | wxALIGN_RIGHT, 5);

SetSizerAndFit(sizer_v);
}

/////////////////////////////////////////////////
// A partir daqui, a interface gráfica já está criada e
// veremos os métodos que realmente importam neste exemplo.

// Método: Envia
// Envia 'msg' para o socket e efetua a leitura logo em seguida, retornando o resultado
wxString MailFrame::Envia(wxSocketBase *socket, wxString msg)
{
// Se estiver desconectado, sai do método e retorna uma string vazia
if (socket->IsDisconnected())
return wxEmptyString;

wxString res;

// Envia 'msg' pelo socket
socket->Write((char *)msg.mb_str(), msg.Len());

// Em caso de erro, retorna uma mensagem avisando e fecha a conexão
if (socket->Error())
{
return wxT(">> Ocorreu um erro ao se comunicar com o servidor!\n");
socket->Close();
}

// Recebe a resposta enviada pelo outro host e a retorna
return msg + Le(socket);
}

// Método: Le
// Lê o conteúdo do socket (ou seja, qualquer mensagem enviada pelo outro host)
wxString MailFrame::Le(wxSocketBase *socket)
{
wxString res;
char buf[BUFFER]; // Buffer para recebimento

do
{
memset(buf, 0, BUFFER);
socket->Read(buf, BUFFER - 1); // Faz a leitura e armazena no buffer
res.Append(buf);
// Enquanto 'res' estiver vazio (nenhuma leitura foi feita ainda) ou
// enquanto houver dados para serem lidos, continuaremos percorrendo o laço
} while ((socket->LastCount() > 0) || (res.Len() == 0));

return res;
}

// Método: MailFrame::Envia
// Envia o e-mail
// Este método vai fazer a conexão, enviar os dados e fechar a conexão
void MailFrame::EnviarMensagem(wxCommandEvent &event)
{
wxIPV4address host;
wxSocketClient sock;
wxString msg, saida;

// Obtém a hora no formato requerido pela RFC822
// O formato é "Dia, data mês ano hora fuso"
// Ex.: Thu, 15 Mar 2007 20:19:00 BRT
char hora[50] = {0};
time_t now = time(NULL);
strftime(hora, 50, "%a, %d %b %Y %T %Z", localtime(&now));

// Configura o objeto 'host'
// Aqui definimos o endereço do servidor e a porta
host.Hostname(txt_servidor->GetValue());
host.Service(txt_porta->GetValue());

// Configuração do socket
// O timeout padrão para operações de E/S será de 120 segundos
// A flag 'wxSOCKET_NOWAIT' indica que operações de E/S irão retornar imediatamente
// (com esta flag, o programa não irá ficar parado em um Read() ou Write() do socket)
sock.SetTimeout(120);
sock.SetFlags(wxSOCKET_NOWAIT);
txt_proto->AppendText(wxT(">> Tentando se conectar...\n"));
if (sock.Connect(host) == false)
{
// Erro na conexão
txt_proto->AppendText(wxT(">> Ocorreu um erro ao tentar conectar no servidor!\n"));
return;
}
txt_proto->AppendText(wxT(">> Conectado!\n"));
txt_proto->AppendText(Le(&sock)); // Lê a mensagem de boas-vindas do servidor

// Neste bloco, enviamos uma identificação (EHLO), o remetente (MAIL From),
// o destinatário (RCPT To) e indicamos o início da mensagem (DATA)
txt_proto->AppendText(Envia(&sock, wxT("EHLO ") + wxGetFullHostName() + wxT("\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("MAIL From: <") + txt_de->GetValue() + wxT(">\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("RCPT To: <") + txt_para->GetValue() + wxT(">\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("DATA\r\n")));

// Agora, o e-mail será montado. O corpo do e-mail tem o seguinte formato:
//
// From: "Nome do remetente" <email_do_remetente>
// To: "Nome do destinatário" <email_do_destinatário>
// Date: Data de envio (obedecendo a RFC822)
// Subject: Assunto do e-mail
//
// Mensagem, linha 1...
// Mensagem, linha 2...
//
// . (deve conter um ponto na última linha para indicar o fim da mensagem)
msg.Clear();
msg.Append(wxT("From: <") + txt_de->GetValue() + wxT(">\r\n"));
msg.Append(wxT("To: <") + txt_para->GetValue() + wxT(">\r\n"));
msg.Append(wxT("Date: ") + wxString(hora) + wxT("\r\n"));
msg.Append(wxT("Subject: ") + txt_assunto->GetValue() + wxT("\r\n\r\n"));
msg.Append(txt_mensagem->GetValue() + wxT("\r\n\r\n"));
msg.Append(wxT("--\r\nVisite A Casa de Just: http://jpjust.blogspot.com\r\n"));
msg.Append(wxT("\r\n.\r\n"));

// O corpo do e-mail é enviado pelo socket e a resposta do servidor é
// inserida na caixa de texto
txt_proto->AppendText(Envia(&sock, msg));

// Por último, fechamos a conexão com o comando QUIT
txt_proto->AppendText(Envia(&sock, wxT("QUIT\r\n")));

// Aguarda que a conexão seja fechada pelo servidor
if (sock.WaitForLost(120) == false)
{
txt_proto->AppendText(wxT(">> Erro ao enviar mensagem!\n"));
}
else
{
txt_proto->AppendText(wxT(">> Mensagem enviada!\n"));
}

// Pronto! O e-mail está enviado! :)
}

IMPLEMENT_APP(MailApp)


quinta-feira, março 08, 2007

Notícias do Blog

Olá, pessoal!

O curso de wxWidgets irá voltar ao andamento normal na próxima semana, pois nestes últimos dias, estive (e ainda estou) fora para resolver pendências. Na volta do curso, postarei sobre comunicação em rede com sockets.

Estive também estudando sobre como usar banco de dados com wxWidgets e postarei sobre isso assim que estiver por dentro do assunto.

E falando em banco de dados, um leitor do blog chamado Guilherme sugeriu o uso da biblioteca SQLAPI++ para acesso a banco de dados com C++. Estive olhando os códigos de exemplo e parece ser bem melhor que as classes do wxWidgets (postarei outro dia sobre o que estou achando do acesso a banco de dados no wxWidgets). Pena que não é gratuita, custa US$ 249,00.

Se alguém conhecer alguma outra alternativa para acesso a banco de dados com C/C++, escreva um comentário. :)

Até o próximo post!

quarta-feira, fevereiro 07, 2007

Curso de wxWidgets, post 13: Formatando texto

Para finalizar o editor de textos, vamos adicionar formatação a ele. Para isto, irei usar uma função que já vimos no curso: wxGetFontFromUser().

Primeiro, vamos incluir o cabeçalho da função:

#include <wx/fontdlg.h>


Vamos também criar um novo método para a formatação. Este método irá se chamar MeuFrame::Formatar(). Vamos declarar a nova função na definição da classe:


class MeuFrame: public wxFrame
{
...
private:
...
void Formatar(void);
}


E agora, implementemos o método:


// Formatação
void MeuFrame::Formatar(void)
{
long inicio, fim;
wxTextAttr attr;
wxFont font;

// Obtém a formatação atual (não suportado em todas as plataformas)
txt_file->GetSelection(&inicio, &fim);
txt_file->GetStyle(inicio, attr);
font = attr.GetFont();

// Altera a formatação
font = wxGetFontFromUser(this, font);
attr.SetFont(font);
txt_file->SetStyle(inicio, fim, attr); // Aplica o estilo ao texto
}


Primeiro, o método tenta obter a formatação atual do texto selecionado (wxTextCtrl::GetStyle() não é suportado em todas as plataformas, testei no Linux e não funcionou). Após obter a formatação, iremos utilizá-la em wxGetFontFromUser() para inicializar os valores da janela, assim, sempre a janela de formatação for aberta, ela estará configurada com a formatação atual.

Em seguida, wxGetFontFromUser() retorna a nova formatação e podemos aplicá-la ao texto selecionado.

Agora devemos criar um botão na barra de ferramentas para este método. Vamos adicionar uma ID para este botão:


// IDs
enum
{
ID_TBAR_NEW,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_FORMAT, // Este foi adicionado
ID_TBAR_ABOUT
};


E criamos o novo botão no construtor de MeuFrame:


tbar->AddTool(ID_TBAR_FORMAT, wxString(wxT("Formatar")), \
wxBitmap(wxT("/usr/share/pixmaps/gedit-icon.png"), wxBITMAP_TYPE_PNG));


Pronto! Está feito. :)



Próximo passo

O editor de textos pára por aqui. Estou querendo falar sobre sockets no próximo post, mas precisarei elaborar um texto maior, pois tem bastante coisa pra explicar. Se alguém quiser aprender algo específico do wxWidgets, postem nos comentários. Assim vocês me ajudam a escolher o próximo assunto. :)

Como sempre faço, o código-fonte completo está abaixo:

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Um editor de textos
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just <jpjust@justsoft.com.br>
* A Casa de Just - http://jpjust.blogspot.com
* 07 de fevereiro de 2007, 20:24, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/fontdlg.h>

// IDs
enum
{
ID_TBAR_NEW,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_FORMAT,
ID_TBAR_ABOUT
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnToolBarClick(wxCommandEvent &event);

// Métodos auxiliares
void Novo(void);
void Abrir(void);
void Salvar(void);
void Formatar(void);
void Sobre(void);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxTextCtrl *txt_file;
wxToolBar *tbar;
wxStatusBar *stbar;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
wxInitAllImageHandlers(); // Inicia todos os hadlers de imagens do wxBitmap

MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_TOOL_RANGE(ID_TBAR_NEW, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Editor de textos - http://jpjust.blogspot.com"))
{
// Sizer e widgets
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Barra de ferramentas
tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);

tbar->AddTool(ID_TBAR_NEW, wxString(wxT("Novo")), \
wxBitmap(wxT("/usr/share/pixmaps/gedit-icon.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_FORMAT, wxString(wxT("Formatar")), \
wxBitmap(wxT("/usr/share/pixmaps/gedit-icon.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();

// Barra de status
stbar = new wxStatusBar(this);
stbar->SetStatusText(wxT("Sem título"));

// Posicionamento
sizer_v->Add(txt_file, 1, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);

// Insere a barra de ferramentas e a barra de status
SetToolBar(tbar);
SetStatusBar(stbar);
}

// Limpa a tela para um novo arquivo
void MeuFrame::Novo(void)
{
int msg = wxMessageBox(wxT("Deseja salvar o arquivo atual antes de criar um novo?"), \
wxT("Novo arquivo"), wxYES_NO | wxCANCEL);

if (msg == wxYES) // Salva
Salvar();
else if (msg == wxCANCEL) // Cancela a operação
return;

// Cria um novo arquivo
txt_file->Clear();
stbar->SetStatusText(wxT("Sem título"));
}

// Abre um arquivo
void MeuFrame::Abrir(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->LoadFile(Filename);

// Atualiza o nome
stbar->SetStatusText(Filename);
}

// Salva o arquivo aberto
void MeuFrame::Salvar(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->SaveFile(Filename);

// Atualiza o nome do arquivo
stbar->SetStatusText(Filename);
}

// Formatação
void MeuFrame::Formatar(void)
{
long inicio, fim;
wxTextAttr attr;
wxFont font;

// Obtém a formatação atual (não suportado em todas as plataformas)
txt_file->GetSelection(&inicio, &fim);
txt_file->GetStyle(inicio, attr);
font = attr.GetFont();

// Altera a formatação
font = wxGetFontFromUser(this, font);
attr.SetFont(font);
txt_file->SetStyle(inicio, fim, attr); // Aplica o estilo ao texto
}

// Janela "Sobre"
void MeuFrame::Sobre(void)
{
wxMessageBox(wxT("Curso de wxWidgets\n\nhttp://jpjust.blogspot.com"), wxT("Sobre"), wxICON_INFORMATION);
}

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_NEW:
Novo();
break;

case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_FORMAT:
Format();
break;
}
}

IMPLEMENT_APP(MeuPrograma)

segunda-feira, janeiro 29, 2007

Curso de wxWidgets, post 12: Adicionando uma barra de status

O post de hoje vai ser bem simples e rápido, iremos apenas adicionar uma barra de status ao nosso editor de textos. Essa barra de status irá informar o nome do arquivo que estamos editando.

Para a barra de status, vamos usar a classe wxStatusBar. O construtor é bem simples:

wxStatusBar(wxWindow* parent, wxWindowID id = wxID_ANY, long style = wxST_SIZEGRIP,
const wxString& name = "statusBar")

parent: A janela que vai conter a barra de status.

id: ID da barra, para os eventos.

style: Estilo da barra. Existe apenas o estilo wxST_SIZEGRIP, que exibe um "puxador" no canto
direito da barra no Windows.

name: Nome da barra.


Nenhum dos parâmetros são obrigatórios, existe o construtor sem parâmetros também. Mas recomenda-se usar this como primeiro parâmetro, para associar a barra com a janela que a está contendo. E é assim que nós iremos fazer no nosso editor de textos. Na definição da classe MeuFrame, temos que adicionar a barra como um atributo em private:

private:
wxStatusBar *stbar;


No construtor de MeuFrame, iremos instanciar o objeto e definir um texto inicial para a barra de status:

// Barra de status
stbar = new wxStatusBar(this);
stbar->SetStatusText(wxT("Sem título"));


O método wxStatusBar::SetStatusText() recebe dois parâmetros: o primeiro, é o texto que você irá colocar na barra, o segundo e opcional, é o número do campo da barra que irá receber o texto.

No nosso exemplo, estamos usando apenas um campo de texto, mas podem existem barras com vários. Você pode ajustar o número de campos da sua barra com o método wxStatusBar::SetFieldsCount(). Por exemplo, se quiser três campos na sua barra, use o método da seguinte forma:

stbar->SetFieldsCount(3);


E para alterar o texto do segundo campo, use:

stbar->SetStatusText(wxT("Campo 2"), 1);
// O primeiro campo é o zero


Como temos apenas um campo em nossa barra, não precisamos especificar o número dele.

Em seguida, devemos "colar" esta barra de status na janela. Fazemos isso com o método wxFrame::SetStatusBar(), ainda no construtor:

SetStatusBar(stbar);


Pronto, a barra já está criada e dentro da nossa janela. Vamos agora atualizar os métodos MeuFrame::Abrir() e MeuFrame::Salvar(), que deverão modificar o texto da barra de status.

No código do nosso editor de textos, procure as linhas:

lb_filename->SetLabel(Filename);


Essas linhas modificam o nome do arquivo no nosso wxStaticText. Iremos remover essas linhas e no lugar delas, colocar:

stbar->SetStatusText(Filename);


Agora o editor de textos já está com a barra pronta. Já que não vamos mais usar o lb_filename, podemos removê-lo do construtor e da definição de MeuFrame.

Compile o novo código e veja a barra em funcionamento:



Código-fonte atualizado

O código completo e atualizado está abaixo. No novo código, eu removi o lb_filename, como mostrado neste post. Também removi btn_load e btn_save, já que agora temos a barra de ferramentas. Além disso, criei o método MeuFrame::Novo(), para criar um novo arquivo e seu respectivo botão na barra de ferramentas. Também removi a mensagem de boas-vindas. Olhe o código inteiro para ver as modificações e entendê-las.

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Um editor de textos
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just <jpjust@justsoft.com.br>
* A Casa de Just - http://jpjust.blogspot.com
* 29 de janeiro de 2007, 9:48, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/textfile.h>

// IDs
enum
{
ID_TBAR_NEW,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_ABOUT
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnToolBarClick(wxCommandEvent &event);

// Métodos auxiliares
void Novo(void);
void Abrir(void);
void Salvar(void);
void Sobre(void);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxTextCtrl *txt_file;
wxToolBar *tbar;
wxStatusBar *stbar;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
wxInitAllImageHandlers(); // Inicia todos os hadlers de imagens do wxBitmap

MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_TOOL_RANGE(ID_TBAR_NEW, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Editor de textos - http://jpjust.blogspot.com"))
{
// Sizer e widgets
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Barra de ferramentas
tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);

tbar->AddTool(ID_TBAR_NEW, wxString(wxT("Novo")), \
wxBitmap(wxT("/usr/share/pixmaps/gedit-icon.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();

// Barra de status
stbar = new wxStatusBar(this);
stbar->SetStatusText(wxT("Sem título"));

// Posicionamento
sizer_v->Add(txt_file, 1, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);

// Insere a barra de ferramentas e a barra de status
SetToolBar(tbar);
SetStatusBar(stbar);
}

// Limpa a tela para um novo arquivo
void MeuFrame::Novo(void)
{
int msg = wxMessageBox(wxT("Deseja salvar o arquivo atual antes de criar um novo?"), \
wxT("Novo arquivo"), wxYES_NO | wxCANCEL);

if (msg == wxYES) // Salva
Salvar();
else if (msg == wxCANCEL) // Cancela a operação
return;

// Cria um novo arquivo
txt_file->Clear();
stbar->SetStatusText(wxT("Sem título"));
}

// Abre um arquivo
void MeuFrame::Abrir(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->LoadFile(Filename);

// Atualiza o nome
stbar->SetStatusText(Filename);
}

// Salva o arquivo aberto
void MeuFrame::Salvar(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->SaveFile(Filename);

// Atualiza o nome do arquivo
stbar->SetStatusText(Filename);
}

// Janela "Sobre"
void MeuFrame::Sobre(void)
{
wxMessageBox(wxT("Curso de wxWidgets\n\nhttp://jpjust.blogspot.com"), wxT("Sobre"), wxICON_INFORMATION);
}

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_NEW:
Novo();
break;

case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_ABOUT:
Sobre();
break;
}
}

IMPLEMENT_APP(MeuPrograma)

terça-feira, janeiro 16, 2007

Justsoft GNU/Linux

Neste dia 15, a Justsoft disponibilizou em seu site a sua distribuição Linux baseada no Debian etch. O Justsoft GNU/Linux vem sido desenvolvido desde dezembro do ano passado, com o objetivo de fornecer uma distribuição voltada aos usuários finais, tanto domésticos como corporativos.

A idéia principal desta distribuição é seguir os requisitos de software do projeto do governo Computador para Todos, com inúmeros aplicativos já instalados, tornando possível a venda de computadores com o Justsoft GNU/Linux pré-instalado.

Além de atender os requisitos do programa do governo, o Justsoft GNU/Linux também inclui outros pacotes extras, que facilitam a vida do usuário, como por exemplo, drivers e aplicativos para impressoras, software para webcam, etc.

O Justsoft GNU/Linux consiste de uma série de meta-pacotes que instalam dependências do repositório Debian, customizando a distribuição (ao estilo Debian CDD). Os usuários podem optar por baixar o CD de instalação no site da Justsoft ou simplesmente instalar o Debian etch e em seguida, adicionar o repositório do Justsoft GNU/Linux:

deb http://apt.justsoft.com.br/justsoft paranoid main


Depois, basta instalar o pacote justsoft-cdd:

# apt-get update
# apt-get install justsoft-cdd


No site da Justsoft pode ser acessada a página da distribuição, com mais informações, download e screenshots.

terça-feira, janeiro 09, 2007

Curso de wxWidgets, post 11: Construindo barras de ferramentas

Agora vamos dar uma incrementada no nosso editor de textos, adicionando uma barra de ferramentas nele. A classe resposável por isto é a wxToolBar.

O procedimento básico é criar a barra, instanciando um objeto da classe e em seguida, atribuir um método ao evento de clique dos botões da barra.

Como sempre, vamos iniciar pelo construtor:

wxToolBar(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = wxTB_HORIZONTAL | wxNO_BORDER,
const wxString& name = wxPanelNameStr)

parent: A janela que conterá a barra.

id: ID da barra.

pos: Posição.

size: Tamanho.

style: O estilo da barra.

name: Nome da barra.


Apesar dos parâmetros pos e size, nós não precisamos especificar tamanho e a posição da barra de ferramentas. Apenas iremos criá-la normalmente e "prendê-la" no topo da janela. Para isto, vamos declarar a barra como um atributo privado da classe do nosso frame e instanciá-la no construtor.

Na definição da classe de MeuFrame:
private:
wxToolBar *tbar;


No construtor de MeuFrame:

tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);


Precisaremos também adicionar IDs para os botões da barra na lista de enum do nosso código:

enum
{
ID_BTNLOAD,
ID_BTNSAVE,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_ABOUT
};


Agora que a barra de ferramentas está criada, podemos adicionar nossos botões a ela. Fazemos isso também no construtor de MeuFrame, depois da instanciação da barra.

Para o nosso editor de textos, iremos adicionar três botões, "Abrir", "Salvar" e "Sobre", além de um separador antes do "Sobre". Isto será feito através do método wxToolBar::AddTool() e wxToolBar::AddSeparator() para o separador:

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();


Perceba que eu usei o método wxToolBar::Realize() após adicionar os botões. Isto irá atualizar a barra.

No código acima, usei algumas imagens que estão instaladas no meu computador. Você pode alterar o caminho das imagens acima para outras existentes no seu sistema. Obviamente, se você está compilando no Windows, os caminhos acima não funcionarão.

No final deste post, falarei um pouco sobre o uso da classe wxBitmap.

Por último, vamos dizer ao frame quem é sua barra de ferramentas. Para isto, usamos o método wxFrame::SetToolBar():

// Insere a barra de ferramentas
SetToolBar(tbar);


Neste ponto, a barra já irá aparecer na janela do editor.

Criando eventos para a barra de ferramentas

A parte mais simples já foi feita. Agora vamos dar vida à barra!

No nosso exemplo do editor de textos, fiz uma modificação para facilitar o código. Criei os métodos Abrir(), Salvar() e Sobre(). Os códigos de carregamento e gravação do arquivo, que antes estavam em OnLoadClick() e OnSaveClick() foram pra Abrir() e Salvar() respectivamente. Sobre() tem um código para exibir um pop-up.

Mas o método mais importante que iremos criar é o MeuFrame::OnToolBarClick(). Veja sua definição:

void OnToolBarClick(wxCommandEvent &event);


Este método tratará os eventos de clique na barra de ferramentas. Antes de mostrar sua implementação, veja a definição do evento na tabela de eventos:

EVT_TOOL_RANGE(ID_TBAR_LOAD, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)


Para os botões normais (wxButton), nós precisamos definir um evento para cada botão. No caso da barra de ferramentas, definimos o range dos IDs dos botões da barra. No nosso exemplo, associamos este eventos a todos os IDs desde ID_TBAR_LOAD até ID_TBAR_ABOUT. Isso é muito útil quando criamos uma barra com muitos botões.

Você pode ver os outros eventos disponíveis para o wxToolBar na documentação da classe.

Mas como na definição, todos os IDs irão chamar o mesmo método, como iremos dar funções diferentes para cada botão da barra? Devemos tratar isto dentro do método. Agora mostrarei sua implementação:

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_ABOUT:
Sobre();
break;
}
}


O switch (event.GetId()) vai criar um switch com a ID do evento. Então, podemos dar diferentes tarefas para os botões dentro do switch, como mostrado acima.

Bem, este é o básico sobre wxToolBar. Nosso editor de texto agora está assim:



Tente modificar as imagens da barra (detalhes abaixo), ou adicionar novos botões. Lembre-se de checar as IDs na definição do evento caso você adicione ou remova botões.

Alguns tópicos sobre o uso do wxBitmap na barra de ferramentas

Não falei sobre wxBitmap ainda, também não vou entrar em detalhes agora, falarei apenas o suficiente para usar imagens no wxToolBar.

A classe wxBitmap serve basicamente para tratar imagens, exibindo-as no seu programa ou em outros widgets. No caso do wxToolBar, nós usamos o wxBitmap para definir as imagens que serão exibidas em cada botão da barra.

No nosso exemplo, eu usei um construtor bastante simples:

wxBitmap(const wxString& name, long type)


Este construtor carrega uma imagem de um arquivo do tipo especificado. Como usei arquivos .PNG, usei o tipo wxBITMAP_TYPE_PNG. Existem diversos outros tipos que você pode encontrar na documentação do construtor.

Mas o wxBitmap também pode tratar tipos de uma outra classe chamada wxImage. O tipo que usei, por exemplo, não é de wxBitmap e sim de wxImage.

O tipo wxBMPHandler está diponível por padrão no programa. Para usar outros tipos (como o wxBITMAP_TYPE_PNG que eu usei), devemos "instalar" eles. Para isto, usamos o método wxInitAllImageHandlers(), que deve ser chamado no método OnInit() da classe principal do nosso programa.

Então, para colocar imagens nos botões da barra de ferramentas, devemos iniciar os handlers para todos os tipos suportados com wxInitAllImageHandlers() no método OnInit() da classe principal. Em seguida, abrir a imagem com wxBitmap e passar o objeto como parâmetro de wxToolBar::AddTool().

Futuramente irei falar melhor sobre wxBitmap e wxImage, mas por enquanto, deixarei apenas o suficiente para usarmos o wxToolBar.

Veja o código-fonte completo do editor de textos no estado atual:

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Um editor de textos
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just
* A Casa de Just - http://jpjust.blogspot.com
* 9 de janeiro de 2007, 22:45, Ilhéus, BA, Brasil.
*/

#include
#include

// IDs
enum
{
ID_BTNLOAD,
ID_BTNSAVE,
ID_TBAR_LOAD,
ID_TBAR_SAVE,
ID_TBAR_ABOUT
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnLoadClick(wxCommandEvent &event);
void OnSaveClick(wxCommandEvent &event);
void OnToolBarClick(wxCommandEvent &event);

// Métodos auxiliares
void Abrir(void);
void Salvar(void);
void Sobre(void);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxButton *btn_load;
wxButton *btn_save;

wxStaticText *lb_filename;

wxTextCtrl *txt_file;

wxToolBar *tbar;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
wxInitAllImageHandlers(); // Inicia todos os hadlers de imagens do wxBitmap

MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTNLOAD, MeuFrame::OnLoadClick)
EVT_BUTTON(ID_BTNSAVE, MeuFrame::OnSaveClick)
EVT_TOOL_RANGE(ID_TBAR_LOAD, ID_TBAR_ABOUT, MeuFrame::OnToolBarClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Arquivos"))
{
// Sizer e widgets
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

btn_load = new wxButton(this, ID_BTNLOAD, wxT("Abrir"));
btn_save = new wxButton(this, ID_BTNSAVE, wxT("Salvar"));

lb_filename = new wxStaticText(this, wxID_ANY,
wxT("Sem titulo.txt"));

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Barra de ferramentas
tbar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TEXT);

tbar->AddTool(ID_TBAR_LOAD, wxString(wxT("Abrir")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-folder.png"), wxBITMAP_TYPE_PNG));

tbar->AddTool(ID_TBAR_SAVE, wxString(wxT("Salvar")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-cd.png"), wxBITMAP_TYPE_PNG));

tbar->AddSeparator();

tbar->AddTool(ID_TBAR_ABOUT, wxString(wxT("Sobre")), \
wxBitmap(wxT("/usr/share/pixmaps/gnome-info.png"), wxBITMAP_TYPE_PNG));

tbar->Realize();

// Posicionamento
sizer_h->Add(btn_load, 0, wxALL, 5);
sizer_h->Add(btn_save, 0, wxALL, 5);
sizer_h->Add(lb_filename, 1, wxALL, 5);

sizer_v->Add(sizer_h, 0, wxALL, 0);
sizer_v->Add(txt_file, 1, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);

// Insere a barra de ferramentas
SetToolBar(tbar);

// Boas-vindas
wxMessageBox(wxT("Seja bem-vindo(a) a mais um maravilhoso programa! :)"),
wxT("Bem-vindo(a)!"), wxICON_INFORMATION);
}

// Abre um arquivo
void MeuFrame::Abrir(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->LoadFile(Filename);

// Atualiza o nome
lb_filename->SetLabel(Filename);
}

// Salva o arquivo aberto
void MeuFrame::Salvar(void)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

txt_file->SaveFile(Filename);

// Atualiza o nome do arquivo
lb_filename->SetLabel(Filename);
}

// Janela "Sobre"
void MeuFrame::Sobre(void)
{
wxMessageBox(wxT("Curso de wxWidgets\n\nhttp://jpjust.blogspot.com"), wxT("Sobre"), wxICON_INFORMATION);
}

// Botão de leitura
void MeuFrame::OnLoadClick(wxCommandEvent &event)
{
Abrir();
}

// Botão de salvamento
void MeuFrame::OnSaveClick(wxCommandEvent &event)
{
Salvar();
}

// Método para a barra de ferramentas
void MeuFrame::OnToolBarClick(wxCommandEvent &event)
{
switch (event.GetId())
{
case ID_TBAR_LOAD:
Abrir();
break;

case ID_TBAR_SAVE:
Salvar();
break;

case ID_TBAR_ABOUT:
Sobre();
break;
}
}

IMPLEMENT_APP(MeuPrograma)

domingo, janeiro 07, 2007

Feliz 2007, o blog está de volta!

Olá meus caros leitores!! FELIZ 2007 (atrasado) A TODOS!!! :)

Como eu mencionei no post anterior, dei uma pausa no curso de wxWidgets devido à quantidade de provas e atividades no curso de Ciência da Computação. Aproveitei e viajei no Natal e Reveillon pra descansar um pouco e agora estou de volta. A partir de terça, o curso de wxWidgets voltará e o blog entrará em ação novamente.

Obrigado a todos que visitaram a minha casa em 2006 e aos que continuaram fazendo-o mesmo durante o período de recesso.

Um abraço e até terça! :)

quarta-feira, dezembro 13, 2006

Curso de wxWidgets: Uma pausa

Olá, pessoal.

Como perceberam, já tem um tempo que não posto nada sobre o curso de wxWidgets. Bem, vou dar uma pausa nele por enquanto, pois estou terminando trabalhos e provas de final de semestre na Universidade e estou meio sem tempo. O curso voltará na segunda semana de Janeiro, sem falta. :)

quinta-feira, novembro 23, 2006

Hotmail no Mozilla Thunderbird

Se você usa o Mozilla Thunderbird e tem conta no Hotmail, provavelmente já teve vontade de baixar suas mensagens pelo software cliente.

Existem vários programas que intermediam a conexão entre o cliente e o Hotmail. Já usei alguns, mas não me agradaram muito. Enfim, encontrei hoje uma extensão para o Thunderbird.

O nome da extensão é WebMail. Basta você instalar a extensão mais o módulo do Hotmail. Essa extensão possui também módulos para outros serviços de webmails.

O primeiro passo é baixar a extensão do WebMail na página de download. Em seguida, baixe o módulo do Hotmail na mesma página. Após instalar as extensões, reinicie o Thunderbird.

O passo seguinte, é configurar a extensão. Clique em Ferramentas -> Extensões. Selecione a extensão do WebMail e clique em Preferências.

Na janela de preferências, certifique-se de que POP e SMTP estão OK (bola verde e texto Running). Caso não estejam rodando, pode ser que você não esteja com permissões para usar portas baixas. Neste caso, clique na aba Ports e escolha portas acima de 1024 para POP e SMTP. Se você precisar modificar as portas, reinicie o Thunderbird novamente.

Por último, adicione sua conta do Hotmail. Basta adicionar a conta normalmente, como você faria com qualquer outra conta POP3. Só que no momento de especificar o servidor POP de entrada, use localhost. Termine o resto do assistente de nova conta normalmente.

Se você mudou as portas POP e SMTP na extensão, você terá que fazer o mesmo na configuração da sua conta. Clique em Editar -> Configuração de contas. Procure sua conta do Hotmail e clique na seção Configurações do servidor. Configure a porta para o mesmo número que você escolheu na configuração da extensão. Em seguida, vá para Servidor de saída (SMTP), lá no final, e adicione o servidor SMTP localhost com a porta SMTP que você escolheu nas configurações da extensão.

Pronto! Seu Thunderbird está pronto para baixar e-mails do Hotmail. :)

segunda-feira, novembro 20, 2006

Curso de wxWidgets, post 10: Mais sobre arquivos com wxTextFile

Agora será apresentada a classe wxTextFile. Essa classe permite manipular arquivos de texto e possui alguns métodos bastante úteis. Como eu havia comentado no post 8, eu senti falta de alguns métodos interessantes em wxFile, como por exemplo, ler uma linha inteira de um arquivo. Só depois que eu descobri a wxTextFile :P

A abertura de um arquivo para leitura se dá com o método wxTextFile::Open():

wxTextFile File;
File.Open("arquivo.txt");


Caso o arquivo não possa ser aberto ou não exista, wxTextFile::Open() retornará falso.

Agora, basta caminharmos pelas linhas e ler o conteúdo do arquivo. Podemos saber o número de linhas em um arquivo com wxTextFile::GetLineCount() (retornando o número de linhas) e ler cada linha com wxTextFile::GetLine() (retornando um objeto wxString com o conteúdo da linha lida). Veja um exemplo de leitura linha a linha:

for (unsigned int line = 0; line < File.GetLineCount(); line++)
txt_file->AppendText(File.GetLine(line) + wxT("\n"));


O laço acima percorre cada linha do arquivo e adiciona em um objeto do tipo wxTextCtrl.

Após ter lido todo o arquivo, podemos fechá-lo com wxTextFile::Close():

File.Close();


Já no caso de gravações, temos que criar o arquivo com wxTextFile::Create():

wxTextFile File;
File.Create("arquivo.txt");


Caso o arquivo não possa ser criado, wxTextFile::Create() retornará falso.

Agora, basta chamar wxTextFile::AddLine() para adicionar linhas no arquivo:

File.AddLine(wxT("Linha 1"));
File.AddLine(wxT("Linha 2"));
File.AddLine(wxT("Linha 3"));


Por último, devemos gravar as alterações no disco e fechar o arquivo:

File.Write();
File.Close();


Existem também outros métodos interessantes:

wxTextFile::GoToLine(size_t n) leva você até a linha n.

wxTextFile::GetFirstLine(), GetNextLine(), GetPrevLine(), GetLastLine() retornam a primeira linha, a próxima, a anterior e a última respectivamente.

Você também pode inserir uma linha em um local específico com wxTextFile::InsertLine() e pode remover qualquer linha com wxTextFile::RemoveLine().

O exemplo de editor de textos

Modifiquei o programa do post 9 para usar wxTextFile no lugar de wxFile. Vejam o programa alterado abaixo:

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Exemplo de um editor de textos usando wxTextFile
*
* O objetivo deste código é demonstrar o uso da classe wxTextFile
* para efetuar leitura e escrita em um arquivo de texto, além
* de mostrar o uso básico das classes wxApp, wxFrame, wxStaticText,
* wxButton, wxTextCtrl e wxBoxSizer, assim como o tratamento de
* eventos e as funções wxFileSelector() e wxMessageBox()
* (tópicos abordados do post 2 ao post 8 do curso).
*
* wxTextCtrl possui dois métodos para carregar e salvar um arquivo
* (wxTextCtrl::LoadFile() e wxTextCtrl::SaveFile()), porém, wxTextFile
* é utilizado para demonstrar seu uso. No trecho onde a leitura e
* escrita são feitas, existem comentários explicando como utilizar
* estes métodos.
*
* Copyright (c) João Paulo Just <jpjust@justsoft.com.br>
* A Casa de Just - http://jpjust.blogspot.com
* 20 de novembro de 2006, 20:25, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/textfile.h>

// IDs
enum
{
ID_BTNLOAD,
ID_BTNSAVE
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnLoadClick(wxCommandEvent &event);
void OnSaveClick(wxCommandEvent &event);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxButton *btn_load;
wxButton *btn_save;

wxStaticText *lb_filename;

wxTextCtrl *txt_file;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTNLOAD, MeuFrame::OnLoadClick)
EVT_BUTTON(ID_BTNSAVE, MeuFrame::OnSaveClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Arquivos"))
{
// Sizer e widgets
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

btn_load = new wxButton(this, ID_BTNLOAD, wxT("Abrir"));
btn_save = new wxButton(this, ID_BTNSAVE, wxT("Salvar"));

lb_filename = new wxStaticText(this, wxID_ANY,
wxT("Sem titulo.txt"));

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Posicionamento
sizer_h->Add(btn_load, 0, wxALL, 5);
sizer_h->Add(btn_save, 0, wxALL, 5);
sizer_h->Add(lb_filename, 1, wxALL, 5);

sizer_v->Add(sizer_h, 0, wxALL, 0);
sizer_v->Add(txt_file, 1, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);

// Boas-vindas
wxMessageBox(wxT("Seja bem-vindo(a) a mais um maravilhoso programa! :)"),
wxT("Bem-vindo(a)!"), wxICON_INFORMATION);
}

// Botão de leitura
void MeuFrame::OnLoadClick(wxCommandEvent &event)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

// Tenta abrir o arquivo com wxTextFile
wxTextFile File;
if (File.Open(Filename) == false)
return;

// Lê o conteúdo do arquivo linha a linha
for (unsigned int line = 0; line < File.GetLineCount(); line++)
txt_file->AppendText(File.GetLine(line) + wxT("\n"));

// Não é preciso todo este código para ler um arquivo
// e exibí-lo em um wxTextCtrl. O trecho acima foi
// escrito apenas para que você entenda a classe wxTextFile.
// wxTextCtrl possui um método próprio para ler o
// conteúdo de um arquivo. Tente comentar o trecho acima
// e usar o método abaixo para fazer a leitura:

//txt_file->LoadFile(Filename);

// Fecha o arquivo
File.Close();

// Atualiza o nome
lb_filename->SetLabel(Filename);
}

// Botão de salvamento
void MeuFrame::OnSaveClick(wxCommandEvent &event)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

// Tenta criar o arquivo com wxTextFile
wxTextFile File;
if (File.Create(Filename) == false)
return;

// Limpa o arquivo, caso exista conteúdo nele
File.Clear();

// Percorre a caixa de texto gravando o conteúdo
// no arquivo aberto, linha a linha
for (unsigned int line = 0; line < txt_file->GetNumberOfLines(); line++)
File.AddLine(txt_file->GetLineText(line));

// Neste ponto, eu também poderia usar um método
// de wxTextCtrl para salvar o arquivo. Tente
// comentar o trecho de gravação acima e descomente
// a linha abaixo para testar:

//txt_file->SaveFile(Filename);

// Salva o arquivo em disco e fecha
File.Write();
File.Close();

// Atualiza o nome do arquivo
lb_filename->SetLabel(Filename);
}

IMPLEMENT_APP(MeuPrograma)

sábado, novembro 11, 2006

Curso de wxWidgets, post 9: Um exemplo de um editor de textos

Resolvi fazer um exemplo de um editor de textos, usando a maioria das classes estudadas até agora. Além de mostrá-las em ação, vou pagar a semana que fiquei sem postar no mês passado :P. O código completo do exemplo está abaixo. Compile e veja o programa funcionando. Não deixe de ler os comentários, existem algumas informações importantes neles.

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Exemplo de um editor de textos usando wxFile
*
* O objetivo deste código é demonstrar o uso da classe wxFile
* para efetuar leitura e escrita em um arquivo de texto, além
* de mostrar o uso básico das classes wxApp, wxFrame, wxStaticText,
* wxButton, wxTextCtrl e wxBoxSizer, assim como o tratamento de
* eventos e as funções wxFileSelector() e wxMessageBox()
* (tópicos abordados do post 2 ao post 8 do curso).
*
* wxTextCtrl possui dois métodos para carregar e salvar um arquivo
* (wxTextCtrl::LoadFile() e wxTextCtrl::SaveFile()), porém, wxFile
* é utilizado para demonstrar seu uso. No trecho onde a leitura e
* escrita são feitas, existem comentários explicando como utilizar
* estes métodos.
*
* Copyright (c) João Paulo Just
* A Casa de Just - http://jpjust.blogspot.com
* 8 de novembro de 2006, 21:03, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/file.h>

#define BUF_SIZE 128 // Tamanho do buffer de leitura/escrita

// IDs
enum
{
ID_BTNLOAD,
ID_BTNSAVE
};

// Classe do programa
class MeuPrograma: public wxApp
{
public:
// Método principal
virtual bool OnInit(void);
};

// Classe do frame
class MeuFrame: public wxFrame
{
public:
// Construtor
MeuFrame(void);

// Eventos
void OnLoadClick(wxCommandEvent &event);
void OnSaveClick(wxCommandEvent &event);

DECLARE_EVENT_TABLE()

private:
// Widgets do frame
wxButton *btn_load;
wxButton *btn_save;

wxStaticText *lb_filename;

wxTextCtrl *txt_file;
};

// Inicialização do programa
bool MeuPrograma::OnInit(void)
{
MeuFrame *frame = new MeuFrame();
frame->Show();
SetTopWindow(frame);
return true;
}

// Tabela de eventos
BEGIN_EVENT_TABLE(MeuFrame, wxFrame)
EVT_BUTTON(ID_BTNLOAD, MeuFrame::OnLoadClick)
EVT_BUTTON(ID_BTNSAVE, MeuFrame::OnSaveClick)
END_EVENT_TABLE()

// Construtor do frame
MeuFrame::MeuFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Arquivos"))
{
// Sizer e widgets
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

btn_load = new wxButton(this, ID_BTNLOAD, wxT("Abrir"));
btn_save = new wxButton(this, ID_BTNSAVE, wxT("Salvar"));

lb_filename = new wxStaticText(this, wxID_ANY,
wxT("Sem titulo.txt"));

txt_file = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

// Posicionamento
sizer_h->Add(btn_load, 0, wxALL, 5);
sizer_h->Add(btn_save, 0, wxALL, 5);
sizer_h->Add(lb_filename, 1, wxALL, 5);

sizer_v->Add(sizer_h, 0, wxALL, 0);
sizer_v->Add(txt_file, 1, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);

// Boas-vindas
wxMessageBox(wxT("Seja bem-vindo(a) a mais um maravilhoso programa! :)"),
wxT("Bem-vindo(a)!"), wxICON_INFORMATION);
}

// Botão de leitura
void MeuFrame::OnLoadClick(wxCommandEvent &event)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxOPEN | wxFILE_MUST_EXIST);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

// Abre o arquivo para leitura com wxFile
wxFile File(Filename, wxFile::read);

// Verifica se o arquivo foi aberto
if (!File.IsOpened())
return;

// Lê o conteúdo do arquivo em partes
// do tamanho de BUF_SIZE
char buffer[BUF_SIZE] = {0};
txt_file->Clear();

while (!File.Eof())
{
// Lê BUF_SIZE - 1 bytes (deixa um espaço
// para o \0 do final da string
if (File.Read(buffer, BUF_SIZE - 1) > 0)
{
// Converte o conteúdo de buffer, que
// deve estar em UTF8 (ANSI) para o formato
// da sua compilação do wxWidgets
wxString Buf(buffer, wxConvUTF7);
txt_file->AppendText(Buf);
memset(buffer, 0, BUF_SIZE);
}
else
{
wxMessageBox(wxT("Erro de leitura!"),
wxT("Erro"), wxICON_ERROR);
return;
}
}

// Não é preciso todo este código para ler um arquivo
// e exibí-lo em um wxTextCtrl. O trecho acima foi
// escrito apenas para que você entenda a classe wxFile.
// wxTextCtrl possui um método próprio para ler o
// conteúdo de um arquivo. Tente comentar o trecho acima
// e usar o método abaixo para fazer a leitura:

//txt_file->LoadFile(Filename);

// Fecha o arquivo
File.Close();

// Atualiza o nome
lb_filename->SetLabel(Filename);
}

// Botão de salvamento
void MeuFrame::OnSaveClick(wxCommandEvent &event)
{
wxString Filename;

// Abre diálogo de arquivo
Filename = wxFileSelector(wxT("Selecione o arquivo"),
wxEmptyString, wxEmptyString, wxEmptyString,
wxT("*.txt"), wxSAVE | wxOVERWRITE_PROMPT);

// Se arquivo inválido, sai do método
if (Filename == wxEmptyString)
return;

// Abre o arquivo para escrita com wxFile
wxFile File(Filename, wxFile::write);

// Verifica se o arquivo foi aberto
if (!File.IsOpened())
return;

// Percorre a caixa de texto gravando o conteúdo
// no arquivo aberto
for (unsigned int line = 0; line <>GetNumberOfLines(); line++)
{
wxString Buf = txt_file->GetLineText(line);
if (File.Write(Buf + wxT("\n"), wxConvUTF7) < 0)
{
wxMessageBox(wxT("Erro de gravação!"),
wxT("Erro"), wxICON_ERROR);
return;
}
}

// Neste ponto, eu também poderia usar um método
// de wxTextCtrl para salvar o arquivo. Tente
// comentar o trecho de leitura acima e descomente
// a linha abaixo para testar:

//txt_file->SaveFile(Filename);

// Fecha o arquivo
File.Close();

// Atualiza o nome do arquivo
lb_filename->SetLabel(Filename);
}

IMPLEMENT_APP(MeuPrograma)


Screenshot