Visões, modelos, colunas, renderizadores, seleções, iteradores. Em um primeiro momento os vários conceitos que permeiam a discussão das árvores em Gtk+ parecem obscuros, mas quando entendemos o por quê da sua existência fica fácil entender que eles estão aí para nos ajudar e não para complicar.
Assim como os elementos gráficos de texto, como vimos anteriormente, as árvores e listas em Gtk+ são construídas com separação da estrutura de dados e das visões dessa estrutura.
Toda árvore em Gtk+ tem o que chamamos de Modelo, que é a estrutura de dados que dá base para a construção da árvore. Esse modelo (GtkTreeModel) é composto por linhas que guardam os dados propriamente ditos como se fosse, de certa forma, um vetor. Os dados são indexados por números. A estrutura que nos permite "andar" por esse modelo é chamada iterador (GtkTreeIter). Ele serve como um tipo de apontador para uma determinada linha (ou nó, para usar um termo mais adequado) da árvore. Vamos construir um esquema conceitual dessas relações:
Note que a razão de estarem ali os nomes ICON_COLUMN (para o "compartimento" que está guardando a figura da pasta) e NAME_COLUMN (para o que está guardando o nome) ficará mais clara logo que falarmos sobre como o Gtk+ representa isso e vermos o código exemplo.
Em contrapartida a essa estrutura de dados, temos os elementos gráficos que os representam. Como vimos anteriormente, no caso do GtkTextView, podemos ter várias visualizações para uma única estrutura de dados GtkTreeModel. Essas visualizações são da classe GtkTreeView. O GtkTreeView é, portanto, o elemento gráfico capaz de representar uma árvore. Para cada dado guardado por um modelo uma visualização tem uma coluna, representada pela GtkTreeViewColumn. O "iterador" da visualização é a seleção feita pelo usuário com o mouse ou teclado (GtkTreeSelection). Vejamos como fica nosso diagrama conceitual:
Veja o código completo enquanto fazemos nosso "tour". Para construir uma árvore em Gtk+ o primeiro passo é pensar em quais colunas queremos ter e dar "nomes" a elas. Faremos uma árvore de diretórios e arquivos e queremos distinguir os diretórios visualmente, então usaremos um ícone e o nome:
NUM_COLUMNS é só uma forma prática de nos referirmos ao número de colunas que criamos, já que um enum começa de 0 e vai crescendo para cada "valor possível". O GdkPixbuf é a estrutura de dados que representará a imagem que usaremos para distinguir os diretórios.
Note no código completo que eu criei uma struct que reúne algumas das variáveis que serão usadas em várias partes do código, assim evito usar variáveis globais e ficar passando muitos argumentos.
Na função criar_interface () é criada a janela principal do nosso programa com a GtkTreeView empacotada como um elemento gráfico normal. Logo depois criamos as colunas (GtkTreeViewColumn) que representarão cada um dos dados do nosso modelo que será criado depois. Vejamos essa etapa com alguns detalhes:
Notamos que a primeira coisa que se faz é criar um renderizador de células. As células são cada um dos compartimentos que contêm os dados de uma determinada linha em relação a uma coluna. Em uma coluna que mostra ícones, por exemplo, cada célula é responsável por renderizar o ícone de uma linha. Por isso criamos para essa coluna um renderizador GtkCellRendererPixbuf.
Ao criarmos a coluna especificamos, além de seu título e renderizador, as propriedades do renderizador terminados por NULL. Por exemplo, para o renderizador da primeira coluna temos de especificar qual dos "índices" da linha do GtkTreeModel contém o ícone. Como já vimos anteriormente, escolhemos que o ícone seria o primeiro dado da linha (0, ou, por causa do enum, PIXBUF_COLUMN), então definimos isso como valor para a propriedade "pixbuf".
As propriedades dos renderizadores específicos podem ser vistas ao final das suas páginas na referência da API. É importante notar novamente que todos os renderizadores são classes derivadas de GtkCellRenderer e, por isso, as propriedades desta são, também, aplicáveis. Para completar essa fase, adicionamos essa coluna à nossa GtkTreeView.
Já criamos a parte visível da nossa árvore. Falta agora a estrutura de dados que dá base para ela. É exatamente isso que a função criar_tree_model () faz. Vamos olhar como:
A criação do modelo precisa de, como primeiro argumento, quantos dados diferentes iremos guardar em cada linha. Os argumentos que seguem são os tipos de dados que serão guardados em cada um dos índices da linha. A ordem deles é muito importante, porque eles serão relacionados às colunas, como vimos. Os tipos usados podem ser encontrados na referência da API do GObject (que contém a implementação de tipos GType) e nos objetos que serão usados, como é o caso do GdkPixbuf.
Por último, a função preencher_arvore () trata pedir à adicionar_diretorio que leia o conteúdo do diretório especificado e de seus subdiretórios montando a árvore. Podemos ver o uso da função gtk_tree_store_set (), que serve para definir o dado para um determinado índice ou índices da linha criada com gtk_tree_store_append ().
A lógica por trás disso é razoavelmente simples: você cria uma nova linha ao final da árvore e a função que faz isso te devolve um apontador para aquela linha (o GtkTreeIter que a função gtk_tree_store_append () modifica). Você usa, então, esse apontador para definir os dados daquela linha, usando -1 como terminador para os pares de número índice e valor.
Podemos ver, também, que o sinal "changed" é conectado à GtkTreeSelection que é o que representa a seleção do usuário em uma determinada GtkTreeView. A lógica no tratador desse evento (mudou_selecao_cb () é bem parecida com a que tratamos acima: você obtém o GtkTreeIter que aponta para aquela seleção e usa os índices para obter os dados daquela linha.
Por fim, algo que é de extrema importância é vermos novamente que a GtkTreeView é apenas uma visualização para uma estrutura de dados que fica por baixo (GtkTreeModel). Você pode experimentar isso usando os botões que estão em baixo da janela desse programa exemplo para criar novas visualizações. Você notará que, ao editar qualquer uma das visualizações, todas as outras são afetadas.
Copyright © Gustavo Noronha Silva, 2004.