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! :)