The K Desktop Environment

3.3. O Esqueleto de Código

Para obter um conceito de como uma aplicação KDE funciona, teremos primeiro que olhar muito atentamente para o esqueleto de código já disponibilizado pelo Assistente de Aplicação. Como nós já vimos, temos um conjunto de ficheiros de código e header que constroem o código inicial para a aplicação e a tornam pronta-a-correr. Assim, a maneira mais fácil de explicar o código é seguir a implementação linha a linha como é processado durante a execução do programa até entrar no ciclo principal de evento e está pronto a receber interacção do utilizador. Depois, iremos observar a functionalidade que permite a interacção do utilizador e como certas coisas funcionam. Esta é provavelmente a melhor forma de explicar o esqueleto e, como é similar a quase todas as aplicações KDE , irá permitir-lhe ler também o código fonte de outros projectos; adicionalmente, você saberá aqui onde alterar que parte do código para fazer com que as suas aplicações se comportem da forma para que foram desenhadas.

3.3.1. A Função main()

Como a aplicação inicia a sua execução entrando na função main(), este será o ponto de partida do nosso exame ao código. A função main() do KScribble é implementada no ficheiro main.cpp e pode também ser encontrada utilizando o Navegador de Classes seleccionando a pasta "Globais", sub-pasta "Funções":

 1  #include "kscribble.h"
 2
 3  int main(int argc, char* argv[]) {
 4    KApplication app(argc,argv,"KScribble");
 5
 6    if (app.isRestored())
 7    {
 8       RESTORE(KScribbleApp);
 9    }
 10   else
 11   {
 12      KScribbleApp* kscribble = new KScribbleApp;
 13      kscribble->show();
 14      if(argc > 1){
 15        kscribble->openFile(argv[1]);
 16      }
 17    }
 18    return app.exec();
 19  }

Agora, o que acontece primeiro é a normal criação do objecto KApplication, que obtém o nome da nossa aplicação KScribble como terceiro parâmetro. Quando se cria um novo KApplication, uma nova instância de KConfig é criada também que é conectada ao ficheiro de configuração em $HOME/.kde/share/config/appname + rc que guarda toda a informação que queremos utilizar quando iniciamos janelas da aplicação. O nome que passamos ao construtor de app será utilizado como o título da janela mais tarde.

Apesar do código de exemplo para tornar a primeira aplicação Qt numa KDE, o código seguinte é ligeiramente diferente. Após o objecto KApplication estar presente, nós testamos se a aplicação é iniciada através do gestor de sessões do kwm ou manualmente através do utilizador. Isto pode ser descoberto quando se chama isRestored() no objecto app, que retorna true (verdade) para gestão de sessão e false (falso) para um início normal.

Como a gestão de sessão é uma característica principal das aplicações KDE e vastamente utilizada pelo esqueleto mas com muito mais para explicar, nós vamos seguir a secção else{} primeiro; depois voltaremos e explicaremos a funcionalidade de sessão num passo seguinte.

3.3.2. Aplicação Iniciada pelo Utilizador

A secção else{} agora cria uma instância da classe KScribbleApp na linha 12. Este objecto é chamado para se mostrar a sí próprio na linha 13 como normal; a linha 14 determina se um argumento de linha de comando foi passado e, como isto é normalmente o nome de um ficheiro, chama o objecto kscribble para o abrir com openFile().

Note que nós não chamamos o método setTopWidget(kscribble) para a nossa aplicação- isto já foi feito pela classe que o KScribbleApp herda. Agora vamos observar o nosso objecto KScribbleApp- o que é e o que é que já disponibiliza? A única coisa que nós sabemos até agora é que tem de ser um Widget para representar o interface do utilizador na janela principal. Vamos ver a implementação da classe de KScribbleApp, que pode ser encontrada no ficheiro kscribble.cpp ou através de um clique no icon da classe no Navegador de Classes. Como a instância é criada pelo construtor. Primeiro que tudo, vemos que herda a classe KTMainWindow, que é parte da biblioteca kdeui. Esta classe em sí herda QWidget, pelo que, como normal, nós temos um widget normal como sendo a janela de topo. O KTMainWindow contém imensas functionalidades de que a classe KScribbleApp tira proveito. Disponibiliza barras de menu, ferramentas , estados e suporte para gestão de sessões. A única coisa que temos que fazer quando a criar sub-classes da KTMainWindow é criar todos os objectos de que necessitamos e criar outro widget que é gerido pela instância KTMainWindow como a vista principal no centro da janela; normalmente este é o local onde o utilizador trabalha tal como numa vista de edição de texto.

3.3.2.1. O Construtor

Vamos observar o código para o construtor e ver como a instância é criada:

 1   KScribbleApp::KScribbleApp()
 2   {
 3     config=kapp->getConfig();
 4	
 5
 6     ///////////////////////////////////////////////////////////////////
 7     // chama inits para invocar todas as outras partes de construção
 8     initMenuBar();
 9     initToolBar();
 10    initStatusBar();
 11    initKeyAccel();
 12    initDocument();
 13    initView();
 14
 15    readOptions();
 16
 17    ///////////////////////////////////////////////////////////////////
 18    // desactiva itens de menu e barra de ferramentas no arranque
 19    disableCommand(ID_FILE_SAVE);
 20    disableCommand(ID_FILE_SAVE_AS);
 21    disableCommand(ID_FILE_PRINT);
 22
 23    disableCommand(ID_EDIT_CUT);
 24    disableCommand(ID_EDIT_COPY);
 25    disableCommand(ID_EDIT_PASTE);
 26  }

Nós vemos que a nossa instância de configuração do KConfig aponta agora para a configuração da aplicação, pelo que podemos operar mais tarde com as entradas de configuração do ficheiro.

Depois, todas as partes da aplicação que são necessárias são criadas pela sua correspondente função membro que é especificada na nossa janela principal:

  • initMenuBar(): constroi a barra de menu,

  • initToolBar(): constroi a barra de ferramentas ,

  • initStatusBar(): cria a barra de estados,

  • initKeyAccel(): define todos os atalhos de teclado para a nossa aplicação através da configuração de teclado global e específica da aplicação

  • initDocument(): cria o objecto documento para a janela da aplicação

  • initView(): cria o widget principal para a nossa vista dentro da janela principal

  • readOptions(): lê todas as definições específicas da aplicação a partir do ficheiro de configuração e inicializa o resto da aplicação tal como a lista de ficheiros recentes, as posições das barras e tamanho da janela.

Finalmente, nós desactivamos alguns comandos que o utilizador pode realizar, porque não podem estar disponíveis no estado actual da aplicação. Como temos agora uma visão genérica de como a janela da aplicação é criada, iremos olhar para os detalhes de como os elementos do utilizador são construidos seguindo os métodos acima.

3.3.2.2. A Barra de Menu

Como mostrado acima, a barra de menu do KScribble é criada pelo método initMenuBar(). Aí, nós criamos um conjunto de QPopupMenus que surgem quando o utilizador selecciona uma entrada de menu. Depois, nós inserimo-las na barra de menu e conectamo-las com as entradas.

Primeiro, nós criamos o nosso recent_file_menu, que irá conter os nomes dos últimos 5 ficheiros abertos. Temos de fazer isto primeiro, porque esta entrada de menu é inserida no file_menu. Quando adicionamos a conecção directamente- apenas obtemos o sinal que é emitido pela entrada de menu com o seu número de entrada e chamamos a slotFileOpenRecent( int ), que então chama o ficheiro correcto a partir da lista de ficheiros recentes para ser aberto.

Depois criamos o nosso menu "Ficheiro". Este será o menu que irá ser visível na barra de menu. As acções standard são então inseridas no menu de popup uma a uma- primeiro os comandos para criar um novo ficheiro, abrir um ficheiro, fechar um ficheiro etc., finalmente "S&ir" para fechar a aplicação. Todas as entradas de menu têm de ser criadas na ordem em que surgem mais tarde, pelo que temos de manter um olho no que queremos ter em que lugar. Como um exemplo, nós olhamos para as seguintes entradas:

 file_menu->insertItem(Icon("fileopen.xpm"), i18n("&Open..."), ID_FILE_OPEN );
 file_menu->insertItem(i18n("Open &recent"), recent_files_menu, ID_FILE_OPEN_RECENT );

A primeira insere a entrada "Abrir...". Como nós queremos te-la como um icon, nós utilizamos o método insertItem() com o nome do icon. Para entender o processo de leitura do icon, nós necessitamos de saber o que ou onde Icon() é declarada- de facto, é uma macro disponibilizada pela classe KApplication:

 #define Icon(x) kapp->getIconLoader()->loadIcon(x)
Adicionalmente, utiliza a seguinte macro internamente para obter acesso ao objecto da aplicação:
 #define kapp KApplication::getKApplication()

Isto significa que o objecto KApplication já contém uma instância de um leitor de Icons- nós apenas temos de ter acesso a ela; depois irá ler o icon correspondente. Como os nossos icons são todos das bibliotecas KDE , nós não temos de nos preocupar com mais nada- eles são instalados no sistema automaticamente, pelo que também não temos de os incluir no pacote da nossa aplicação para os utilizar.

Após o parâmetro do icon (que é opcional), nós inserimos o nome da entrada de menu através de i18n("&Abrir..."). Aí, nós temos de observar duas coisas: primeiro, a entrada é inserida com o método i18n(). Tal como a entrada Icon(), é uma macro também definida em kapp.h e chama o objecto KLocale da KApplication para traduzir a entrada para o idioma actualmente utilizado:

 #define i18n(X) KApplication::getKApplication()->getLocale()->translate(X)

Chegando aqui, deverá ser mencionado que uma pessoa poderia pensar "Eu não quero utilizar macros"- você pode fazer isso a maior parte dos casos. Mas aqui é imprescindível a utilização da i18n() porque para a internacionalização os ficheiros de idioma respectivos têm de ser construidos. Como este processo de construção depende da string (conjunto de caracteres) i18n, você tem de utilizar a macro.

Como já deve ter adivinhado, o & (i comercial) dentro das entradas de menu é mais tarde interpretado como uma linha por baixo da letra seguinte na entrada de menu. isto permite um acesso rápido ao comando de menu através do teclado quando o utilizador prime a tecla Alt em conjunto com a letra sublinhada.

Finalmente, nós damos um ID (número identificativo) à entrada de menu, que é um número inteiro através do qual podemos encontrar a entrada mais tarde. Para manter um controlo sobre os valores utilizados, estes são definidos através de macros e são coleccionados no ficheiro resource.h dentro do seu projecto. Por consistência, estas macros são todas maiúsculas e começam por ID_, depois o nome do menu seguido pela entrada. Isto torna muito fácil lembramo-nos do sentido de cada entrada em qualquer ponto do código, pelo que não temos de voltar à implementação da barra de menu de novo para observar as entradas.

A segunda entrada de exemplo mostra outra variante do método insertItem(). Aqui, nós acrescentamos o menu de popup recent_files_menu como um item de menu. Isto significa, que a entrada mostra-se a sí própria com a string dada "Abrir recentes", seguida de uma seta para a direita. Na selecção, o menu de popup de ficheiros recentes surge e o utilizador pode escolher o último ficheiro.

Por fim mas não menos importante existem muitas outras formas de inserir items de menu- o esqueleto de aplicação mantém isto o mais simples possível. Mais informação pode ser obtida na documentação Qt sobre a classe QMenuData.

Agora, após termos criado os menus de popup file_menu, edit_menu e view_menu, temos de incluir também um menu "Ajuda". Nós podiamos fazer isto tal como os outros, mas a classe KApplication oferece um método mais rápido e agradável para cobrir isto:

 help_menu = kapp->getHelpMenu(true, i18n("KScribble\n" VERSION ));

Isto é tudo o que temos de fazer para obter um menu de ajuda que contenha uma entrada para o conteudo da ajuda com o atalho de teclado F1, uma caixa-sobre para a aplicação e uma caixa-sobre para o KDE (que pode ser desactivada chamando getHelpMenu(false,...);). O conteudo para a caixa-sobre da nossa aplicação é definida de novo com a string i18n() - VERSION fica com a macro que é definida para a numeração de versão do projecto no ficheiro config.h, pelo que não temos de modificar isto manualmente de cada vez que queremos fazer uma nova distribuição. Esteja à vontade para adicionar aqui qualquer informação sobre a sua aplicação, por ex. o seu nome, endereço email, copyright e afins.

Agora apenas temos de inserir os pop-ups na barra de menu. Como o KTMainWindow já constroi uma barra de menu para nós, nós apenas os inserimos chamando menuBar()->insertItem();.

O que resta fazer é conectar as entradas de menu com os métodos que irão executar. Assim, nós conectamos cada menu de popup através do seu sinal activated( int ) a um método commandCallback( int ), que contém uma frase switch que chama o método correspondente para as entradas de menu. Adicionalmente, nós conectamos os pop-ups através do seu sinal highlighted( int ) para disponibilizar ajuda de barra de estados para cada entrada. Sempre que o utilizador move o seu rato ou focus de teclado para uma entrada, a barra de estados mostra então a correspondente mensagem de ajuda.

Após termos terminado com a barra de menu, podemos continuar com a barra de ferramentas na secção seguinte. Lembre-se que uma instância de KTMainWindow apenas pode ter uma barra de menu visível de cada vez; assim se deseja construir diversas barras de menu, você tem de cria-las separadamente com instâncias de KMenuBar e definir uma delas através dos métodos correspondentes de KTMainWindow como sendo a barra de menu actual. Veja a documentação de classes da KMenuBar para informação mais detalhada sobre como extender as características, veja também Configurar Barras de Menu e Ferramentas.

3.3.2.3. A Barra de Ferramentas

A criação de barras de ferramenta s é agora ainda mais simples que a das barras de menu. Como a KTMainWindow já disponibiliza barras de ferramenta s, que são criadas pela primeira inserção, você é livre de criar várias. Basta adicionar os botões para as funções que deseja disponibilizar:

 toolBar()->insertButton(Icon("filenew.xpm"), ID_FILE_NEW, true, i18n("New File") );

Isto adiciona um botão alinhado à esquerda com o icon "filenew.xpm" com o respectivo ID para a barra de ferramentas . O terceiro parâmetro decide se o botão deverá estar activo ou não; por defeito nós definimos isto para true (verdade), porque o nosso método disableCommand() no final do construtor faz isto por nós automaticamente tanto para as entradas do menu como da barra de ferramentas . Finalmente, o último parâmetro é utilizado como uma "Dica Rápida"- quando o utilizador move o ponteiro do rato sobre o botão de forma a ficar iluminado, uma pequena janela surge que contém uma pequena mensagem de ajuda, cujo conteudo pode ser definido aqui.

Finalmente, todos os botões da barra de ferramentas estão conectados novamente ao nosso método commandCallback() através do seu sinal clicked(). Ao sinal pressed(), nós permitimos ao utilizador receber a mensagem de ajuda correspondente na barra de estados.

Informação Adicional:

Como as barras de ferramentas são criadas utilizando a classe KToolBar, você deverá ler a respectiva documentação. Com a KToolBar, imensas coisas necessárias na barra de ferramentas podem ser realizadas tais como pop-ups demorados se o seu botão deseja fazer surgir um menu quando o botão é mantido premido ou até mesmo widgets como caixas de escolha. Também, por defeito, a barra de ferramentas preenche a largura total da janela, o que a faz ter um aspecto melhor utilizando apenas uma barra. Quando se usa mais do que uma, você deverá também pensar na hipótese de definir o tamanho da barra para terminar a seguir ao botão mais à direita, para que outras barras possam ser apresentadas na mesma linha por baixo da barra de menu. Iremos discutir algumas tecnicas sobre desing e extensão de barras de ferramentas na secção Configurar Barras de Menu e Ferramentas.

3.3.2.4. A Barra de Estados

A barra de estados é, bem como as outras barras, já disponibilizada pela instância KTMainWindow, pelo que apenas temos de inserir os nossos itens como quisermos. Por defeito, o esqueleto contém apenas uma entrada que mostra a ajuda de barra de estados. Para muitas aplicações isto pode não chegar; então você inserirá as entradas que necessite para mostrar por ex. coordenadas e afins.

Também, uma aplicação pode apenas ter uma barra de estados de cada vez como as barras de menu. Se desejar construir várias, deverá cria-las separadamente e definir a barra corrente através do método correspondente da KTMainWindow. A barra de estados também permite inserir widgets, que podem ser utilizador para produzir bons hábitos de apresentar barras de progresso como o KDevelop faz. Verifique a documentação de classes da KStatusBar.

3.3.2.5. Atalhos de Teclado

Ao atingir o método initKeyAccel(), já construimos os itens standard da janela principal de uma aplicação- as barras de menu, ferramentas e estados. Na verdade, não definimos nenhuns de teclado através dos quais utilizadores avançados que apenas queiram trabalhar com o teclado possam aceder rapidamente a certos comandos que são utilizados mais frequentemente durante a utilização do programa. Para o fazer, nós podiamos ter inserido as teclas de na inserção dos itens de menu por exemplo, mas o KDE oferece uma boa solução para construir e manter de teclado. Imensos utilizadores querem te-los configuráveis por um lado e por outro os standard deverão ser os mesmos em todas as aplicações. Assim, o Centro de Controlo do KDE permite configurar de teclado standard globalmente através da utilização da classe KAccel. Adicionalmente, as bibliotecas KDE contêm um widget que permite aos utilizadores configurar de teclado específicos da aplicação facilmente. Como o esqueleto da aplicação apenas utiliza itens de menu que têm acções standard tais como "Novo" ou "Sair", estes são definidos pelo método initKeyAccel(). Acções standard apenas têm de ser conectadas, para os valores de teclado específicos da sua aplicação, têm de os inserir primeiro especificando o nome do de teclado e depois conecta-lo. como os nossos estão todos presentes na barra de menu, temos de modificar os para as entradas de popup. Finalmente chamamos readSettings(), que lê as definições correntes a partir da janela principal do KDE contendo as configurações de standard, e depois as definições de específicos do ficheiro de configuração da aplicação. Quando nós aprofundarmos mais o nosso projecto de exemplo, iremos também falar sobre como configurar os específicos da nossa aplicação através de um diálogo de configuração, veja Configurar Barras de Menu e Ferramentas para essa parte do processo de desenvolvimento.

3.3.2.6. O Modelo de Vista de Documento

As duas próximas chamadas de funções membros, initDocument() e initView(), vão finalmente construir a parte que é suposto as janelas da aplicação disponibilizarem ao utilizador: um interface para trabalhar com os dados que é suposto a aplicação manipular; e essa é também a razão pela qual o esqueleto da aplicação contém três classes, uma classe *App, *View e *Doc. Para entender, porque esta estrutura é útil, iremos olhar um pouco à margem do código e introduzir alguma teoria, depois voltaremos ao programa de novo para ver como o esqueleto feito pelo KDevelop suporta esse modelo.

Basicamente, tudo o que tem sido explicado até agora sobre a estrutura é que necessitamos de uma instância de aplicação que contenha a janela principal. Esta janela é responsável por disponibilizar o interface basico para o utilizador- contém as barras de menu, ferramentas e estados e o controlo de eventos para a interacção do utilizador. Também, contém uma área, que é descrita como uma "vista". Agora, o objecto da visra é genericamente, mostrar os dados que o utilizador pode manipular, por ex. uma parte de um ficheiro de texto. Apesar do ficheiro de texto ser provavelmente maior que a vista é capaz de mostrar no ecrã, disponibiliza ao utilizador a possibilidade de ir para a parte que deseja ver (pelo que é uma vista), e aí o utilizador pode modificar os dados do conteudo do ficheiro. Para dar ao programador uma maneira melhor de separar as partes da aplicação através do código, o Modelo Vista-Documento foi inventado. Apesar de não ser um standard, disponibiliza uma estrutura de como uma aplicação deveria funcionar:

  • A aplicação contém um objecto controlador,

  • um objecto de Vista que mostra os dados com que o utilizador trabalha

  • e um objecto Documento que contém na realidade os dados a serem manipulados.

De regresso ao exemplo de trabalhar com um ficheiro de texto- aí, este modelo iria funcionar de forma a que o Documento leria o conteudo do ficheiro e disponibilizar os métodos para modificar os dados bem como para gravar o ficheiro de novo. A Vista então processa os eventos que o utilizador produz através do teclado e rato e utiliza os métodos do objecto Documento para manipular os dados do documento.

Finalmente, o objecto controlador é responsável pela interacção do utilizador disponibilizando os objectos de Vista e de Documento bem como os interfaces para enviar comandos tais como abrir e gravar. Adicionalmente, certos métodos do objecto Vista podem ser disponibilizados por comandos que podem ser acedidos através de de teclado ou pelo rato ou barras de menu e ferramentas .

Este Modelo Vista Documento tem algumas vantagens- separa o código do programa de uma forma mais orientada por objectos e por isso oferece mais flexibilidade em geral, por ex. o mesmo objecto documento poderia ser apresentadps por duas vistas ao mesmo tempo; quer através de uma nova vista numa nova janela ou através de mosaico da janela corrente que contém dois objectos vista que constroem a região de vista da janela actual.

Agora, se você vem dos sistemas MS-Windows poderá teralguma experiência com isso- o MFC já disponibiliza um modelo de documento que está pronto a utilizar. Para as aplicações KDE e Qt , as coisas são ligeiramente diferentes. A Qt é um poderoso conjunto de ferramentas pois disponibiliza as classes mais necessárias, widgets etc. Mas não existiu nenhuma intenção de tomar conta do modelo documento-vista, e como o KDE herda a Qt , também não existiram nenhumas tendências de introduzir este modelo. Isto de certa forma tem a sua justificação no facto de que normalmente as aplicações X não funcionam com um MDI (Interface de Documento Multiplo). Cada janela principal é responsável pelos seus dados e isso reduz a necessidade de um modelo de documento pelo facto de que métodos para trabalhar em documentos são sempre implícitos em widgets. A única excepção a isto actualmente é o projecto KOffice que tenciona disponibilizar um conjunto de aplicações de produtividade completo tais como um processador de texto, uma folha de cálculo etc. Tecnicamente, isto é realizado com duas alterações à utilização normal da Qt e KDE:

  • o KOffice utiliza KOM e a implementação gratuita de CORBA chamada MICO para comunicação de objectos,

  • as aplicações KOffice utilizam o modelo documento-vista para permitir a todas as aplicações trabalhar com quaisquer objectos de dados do KOffice

Mas como o KDevelop actualmente se foca na utilização das actuasi bibliotecas do KDE 1.1.x e a Qt 1.4x, nós não podemos utilizar este modelo por defeito- isto virá em distribuições futuras de um KDE 2, que irá (esperamos) conter duas modificações principais em relação à situação actual:

  1. um interface MDI para a KTMainWindow

  2. as bibliotecas KOM que disponibilizam um modelo de documento

Assim, a forma actual para os programadores de aplicações pode ser ou implementarem todos os métodos de documentos necessários dentro da sua vista ou tentarem reproduzir um modelo de documento por si. O KDevelop assim contém essa reprodução disponibilizando as classes necessárias e os métodos básicos que são geralmente utilizados para o Modelo Documento-Vista com a estrutura de aplicações para Qt e KDE.

De regresso ao código, você pode agora imaginar o propósito dos dois métodos que mencionamos no início desta secção: as funções initDocument() e initView(). O initDocument() constroi o objecto documento que representa a janela de dados da aplicação e inicializa atributos básicos tais como definir o bit de modificação que indica se os dados correntemente em utilização foram modificados pelo utilizador. Depois, o método initView() constroi o widget *View, conecta-o com o documento e chama o método setView() de KTMainWindow para dizer à janela *App para utilizar o widget *View como a sua vista central.

Para o programador, é importante saber que durante o processo de desenvolvimento ele tem de:

  • re-implementar os métodos virtuais para eventos de rato e teclado disponibilizados pela QWidget no objecto *View para disponibilizar meios de manipular os dados,

  • re-implementar o paintEvent() da QWidget no objecto *View para repaint() (re-desenhar) a cista após modificações,

  • completar a implementação para imprimir o documento através do método de impressão do objecto *View,

  • adicionar a serialização para o objecto *Doc para disponibilizar abertura e gravação de ficheiros,

  • adicionar a implementação da estrutura de dados do documento ao objecto *Doc que está a representar os dados do documento logicamente em memória.

  • adicionar quaiquer métodos que tenham de ser acessíveis pelo utilizador através de de teclado e menus/barras de ferramentas .

3.3.2.7. Configuração da Aplicação

Agora, depois de termos criado todas as instâncias da instância KTMainWindow da nossa aplicação para criar a primeira janela, nós temos de inicializar alguns valores que influenciam a aparência do programa. Para isto, chamamos readOptions(), que obtém todos os valores e chama os métodos necessários para definir os atributos correspondentes. A biblioteca KDE-Core contém a classe KConfig que disponibiliza uma boa possibilidade de guardar valores em ficheiros de configuração bem como de os ler novamente. Também, como cada instância KApplication já cria o seu ficheiro de recursos, apenas temos de aceder a este ficheiro e criar os nossos valores. Como o KConfig nos disponibiliza o objecto de ficheiro, temos de utilizar a classe KConfigBase para ler e escrever todas as entradas. Como escreve-lo é muito fácil de fazer com métodos writeEntry(), ler depende do tipo de atributo que queremos inicializar. Geralmente, uma entrada no ficheiro de configuração contém um nome de valor e o valor. Valores que pertençam conjuntamente num contexto podem ser coleccionados em grupos, pelo que temos de definir o nome do grupo antes de acedermos ao seu valor; o grupo tem de ser definido apenas uma vez para ler um conjunto de atributos que estão no mesmo grupo. Vamos observar o que queremos ler:

 1   void KScribbleApp::readOptions()
 2   {
 3
 4      config->setGroup("General Options");
 5
 6      // bar status settings
 7      bool bViewToolbar = config->readBoolEntry("Show Toolbar", true);
 8      view_menu->setItemChecked(ID_VIEW_TOOLBAR, bViewToolbar);
 9      if(!bViewToolbar)
 10       enableToolBar(KToolBar::Hide);
 11
 12     bool bViewStatusbar = config->readBoolEntry("Show Statusbar", true);
 13     view_menu->setItemChecked(ID_VIEW_STATUSBAR, bViewStatusbar);
 14     if(!bViewStatusbar)
 15       enableStatusBar(KStatusBar::Hide);
 16
 17     // bar position settings
 18     KMenuBar::menuPosition menu_bar_pos;
 19     menu_bar_pos=(KMenuBar::menuPosition)config->readNumEntry("MenuBar Position", KMenuBar::Top);
 20
 21     KToolBar::BarPosition tool_bar_pos;
 22     tool_bar_pos=(KToolBar::BarPosition)config->readNumEntry("ToolBar Position", KToolBar::Top);
 23
 24     menuBar()->setMenuBarPos(menu_bar_pos);
 25     toolBar()->setBarPos(tool_bar_pos);
 26
 27     // initialize the recent file list
 28     recent_files.setAutoDelete(TRUE);
 29     config->readListEntry("Recent Files",recent_files);
 30
 31     uint i;
 32     for ( i =0 ; i < recent_files.count(); i++){
 33       recent_files_menu->insertItem(recent_files.at(i));
 34     }
 35
 36     QSize size=config->readSizeEntry("Geometry");
 37     if(!size.isEmpty())
 38       resize(size);
 39   }

Como vimos numa das partes de código acima, a primeira acção que o nosso construtor faz é:

 config=kapp->getConfig();

que define o ponteiro config tipp KConfig para a configuração da aplicação. Assim, não temos de nos preocupar com a localização do ficheiro de configuração. Na verdade, o ficheiro está, de acordo com o Standard de Sistema de Ficheiros KDE (KDE FSS), localizado em $HOME/.kde/share/config/; iremos observar mais atentamente o KDE FSS num passo futuro quando estivermos a definir localizações de ficheiros do projecto para instalação. Como o ficheiro de configuração é colocado no directório principal do utilizador, cada utilizador tem a sua aparência personalizada da aplicação excepto para valores que estão localizados num ficheiro de configuração para todo o sistema que pode ser criado opcionalmente e instalado pelo programador no directório do KDE. Mas, apesar de isto poder ajudar nalguns casos, devemos evitar quaiquer dependências da nossa aplicação no sentido da existência de entradas de ficheiro. Assim, todos os métodos de leitura disponibilizados pelo KConfigBase permitem adicionar um valor de defeito a ser utilizado quando a entrada não existe. Outra coisa importante para um programador é que o ficheiro de configuração é guardado em texto simples, e isto é também por algumas razões pois tem de ter em atenção alguns critérios:

  • o utilizador pode modificar o ficheiro de configuração através de um simples editor de texto

  • se o utilizador quiser modificar valores manualmente, as entradas deverão ser muito transparentes para determinar o seu propósito

  • para entradas que tenham de ser gravadas, mas são criticas em termos de segurança tal como chaves de acesso, você tem de procurar uma solução adequada para assegurar a segurança.

Agora que sabemos os básicos, vamos analizar o código. Como dissemos, apenas temos de utilizar o nosso ponteiro config para aceder aos valores. Primeiro, na linha 4, nós definimos o grupo actual para "Opções Gerais". Isto indica que os valores estão de certo modo atributos gerais para a aplicação. Depois nós lemos os valores para a barra de ferramentas e estados- estes têm de ser gravados quando a aplicação fecha para repor os seus estadps de novo quando o utilizador reiniciar o programa. Como as barras apenas podem estar ligadas ou desligadas, nós utilizamos um valor boolean, pelo que, o nosso método é readBoolEntry(). O processo é idêntico para ambas as barras, pelo que apenas temos de olhar para as linhas 7-10 para observar o que está a acontecer para a barra de ferramentas . Primeiro, nós lemos o valor para uma variável temporária bViewToolbar na linha 7. O nome do valor no ficheiro é "Mostrar Barra de Ferramentas" e, se o valor não estiver presente (o que seria o caso na primeira vez que a aplicação iniciasse), o valor de defeito é definido para true (verdade). De seguida, nos definimos a marca de verificação da entrada de menu para (des)activar a barra de ferramentas através deste valor: nós chamamos setItemChecked() no menu ver, entrada ID_VIEW_TOOLBAR com o nosso atributo. Finalmente, definimos a barra de ferramentas para utilizar o valor. Por defeito, a barra de ferramentas é visível, pelo que, apenas temos de fazer algo se bViewToolbar for false (falso). Com enableToolBar() (linha 10) estamos a definir a barra para se esconder automaticamente se for desactivada.

De seguida, temos de ler as posições da barra. Como o utilizador pode ter modificado a posição da barra arrastando-a com o rato para outra área de cista, esta tem de ser gravada também e o seu estado reposto. Olhando para as classes KToolBar e KMenuBar, vemos que as posições da barra podem ser:

 enum BarPosition {Top, Left, Bottom, Right, Floating, Flat}
 /* {Topo, Esquerda, Baixo, Direita, Flutuante, Fixa} */

Como este valor foi escrito num valor numérico, temos de o ler com readNumEntry() e converte-lo num valor de posição. Com setMenuBarPos() e setBarPos() dizemos às barrass ode aparecerem.

Agora provavelmente notou que o nosso menu "Ficheiro" contém um menu para ficheiros utilizados recentemente. Os nomes de ficheiros são guardados numa lista de strings (conjunto de caracteres), que tem de ser gravado ao fecho da aplicação e agora tem de ser lido para repor o menu. Primeiro, inicializamos a lista com as entradas guardadas utilizando o readListEntry(). Depois, num ciclo for, criamos uma entrada de menu para cada item da lista.

Finalmente, apenas temos de tomar conta da geometria da nossa janela. Nós lemos na aparência através de uma variável QSize contendo um valor x e y para o comprimento e altura da janela. Como a janela é inicializada através da KTMainWindow, não temos de nos preocupar com o valor de defeito e apenas utilizamos resize() se a entrada não estiver vazia.

O que resta explicar na construção da nossa aplicação é que inicialmente temos de desactivar comandos do utilizador disponíveis que o não podem estar no caso de algumas instâncias não corresponderem aos critérios necessários. Estes são gravação de ficheiros e operações que utilizem a área de transferência. Durante o tempo de vida da aplicação, temos de tomar conta destes várias vezes, mas isso é bastante fácil. A estrutura apenas nos dá dois métodos para (des)activar itens de barras de menu e ferramentas com apenas uma chamada de método de cada vez.

3.3.2.8. Executar

Durante a secção anterior, apenas veriicaos o que acontecia durante a chamada de construção da nossa instância do KScribbleApp que nos disponibiliza a janela principal. Após regressar à função main(), temos de chamar show() para mostrar a janela. O que é diferente de qualquer KApplication ou QApplication aqui é que quando estamos a utilizar KTMainWindow como a instância para o nosso widget principal, não temos de o definir com setMainWidget(). isto é feito pelo próprio KTMainWindow e não temos de nos preocupar com isso. A única coisa que resta então é interpretar a linha de comandos. Obtemos a opção de linha de comandos e perguntamos, se int argc é > 1, o que indica que o utilizador chamou a nossa aplicação com kscribble filename_to_open. A nossa janela recebe então o pedido de abertura do ficheiro pelo se nome e chama openDocumentFile() com o nome do ficheiro.

A última linha da função main() faz o trabalho conhecido: executa a instância da aplicação e o programa entra no ciclo de evento.

Agora, na secção A Função main(), nós começamos a separar o processo de execução através de if( app.isRestored() ) e descrevemos o processo normal de invocação. De seguida daremos uma introdução à gestão de sessão e como a nossa aplicação a utiliza.

3.3.3. Invocação por Gestão de Sessão

Como dissemos, a função main() testa, se a aplicação foi invocada pelo gostor de sessão. O gestor de sessão é responsável por gravar o estado actual de todas as janelas de aplicação abertas no ambiente de trabalho do utilizador e tem de os repor quando o utilizador entrar da próxima vez, o que significa que a aplicação não é iniciada pelo utilizador mas invocada automaticamente. A parte do código executado foi:
 6    if (app.isRestored())
 7    {
 8       RESTORE(KScribbleApp);
 9    }

Em A Função main(), nós afirmamos que nós testamos a invocação perguntando à app.isRestored(). Depois a linha 8 é executada. Parece uma afirmação simples, mas de facto irá resultar num processo de execução complexo que queremos seguir nesta secção.

A própria RESTORE() é uma macro disponibilizada pela KTMainWindow. Expande-se para o seguinte código:

 if (app.isRestored()){
   int n = 1;
   while (KTMainWindow::canBeRestored(n)){
     (new KScribbleApp)->restore(n);
     n++;
   }
 }

Isto irá repor todas as janelas de aplicação da classe KScribbleApp criando as instâncias e chamando restore() para a nova janela. É importante compreender que se a nossa aplicação utiliza vários widgets diferentes que herdam KTMainWindow, você tem de expandir a macro e determinar o tipo de widgets de topo utilizando KTMainWindow::classNameOfToplevel(n) em vez da classe KScribbleApp. O método restore() lê então a parte do ficheiro de sessão que contém a informação sobre a janela. Como a KTMainWindow guarda tudo isto por nós, não temos de nos preocupar com mais nada. Apenas informação que pertence à nossa instância específica do KScribbleApp tem de ser então encontrada. Normalmente isto seria um ficheiro temporário que foi criado para guardar o documento ou outra initialização de que poderiamos necessitar. Para obter esta informação de reposição, nós apenas temos de passar por cima de dois métodos virtuais de KTMainWindow, saveProperties() e readProperties(). A informação que temos de gravar no final da sessão é se o documento correntemente aberto está ou não modificado e o nome do ficheiro. Se o ficheiro estiver modificado, obteremos um nome de ficheiro temporário para o gravar. No início da sessão, esta informação é agora utilizada para repor o conteudo do documento:

 void KScribbleApp::readProperties(KConfig*)
 {
   QString filename = config->readEntry("filename","");
   bool modified = config->readBoolEntry("modified",false);
   if( modified ){
     bool b_canRecover;
     QString tempname = kapp->checkRecoverFile(filename,b_canRecover);
 
     if(b_canRecover){
       doc->openDocument(tempname);
       doc->setModified();
       QFileInfo info(filename);
       doc->pathName(info.absFilePath());
       doc->title(info.fileName());
       QFile::remove(tempname);
     }
   }
   else if(!filename.isEmpty()){
   doc->openDocument(filename);
   }
   setCaption(kapp->appName()+": "+doc->getTitle());
 }		
Aqui, a linha kapp->checkRecoverFile() parece um pouco estranha, pois o b_canRecover não está inicializado. Isto é feito pelo método que o define para true, se existir um ficheiro de reposição. Como apenas gravamos um documento num ficheiro de recuperação se foi modificado, definimos o bit de modificado directamente para indicar que a informação não foi gravada para o ficheiro correspondente. Também temos de ter em conta que o ficheiro de recuperação tem outro nome de ficheiro diferente do ficheiro original que foi aberto. Assim, temos de colocar o nome de ficheiro e caminho do antigo nome de ficheiro. Finalmente, temos a informação que queriamos recuperar e podemos apagar o ficheiro temporário através do gestor de sessão.

Sumário:

Durante este capítulo, ficou a saber como a aplicação se inicia quer por invocação normal do utilizador ou pelo gestor de sessão. Verificamos todo o código para aprender como as partes do interface visual da aplicação são construidas bem como inicializar os atributos através de entradas em ficheiros de configuração. Agora podemos executar a aplicação esqueleto para testar estas funções e ver como a janela do programa reage.