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