>A biblioteca 'tdecore' é a plataforma aplicacional básica para todos os programa baseados no KDE. Ela fornece o acesso ao sistema de configuração, ao tratamento da linha de comandos, o carregamento e manipulação de ícones, alguns itens básicos de comunicação entre processos, tratamento de ficheiros e muitos outros utilitários. </para
> fornece vários elementos gráficos e janelas que o Qt não tem ou que tem mas com menos funcionalidades. Inclui também vários elementos itens que são sub-classes das versões do Qt e que se integram melhor com o ambiente do KDE no que respeita às preferências do utilizador. </para
> contém funcionalidades para E/S assíncronas e transparentes na rede, assim como o tratamento de tipos MIME. Também contém a janela de ficheiros do KDE e as suas classes auxiliares. </para
> contém a componente TDEHTML, um elemento de navegação em HTML, com uma API e um processador de DOM e que inclui interfaces para Java e JavaScript. </para
>O modelo de imagens de baixo nível do Qt é baseado nas capacidades oferecidas pelo X11 e por outros sistemas de janelas para os quais o Qt foi implementado. Mas também as extende, implementando funcionalidades adicionais como as transformações arbitrárias por afinidade para texto e imagens. </para>
<para
>A classe gráfica central para o desenho 2D com o Qt é a <ulink url="kdeapi:qt/QPainter"
>QPainter</ulink
>. Ela poderá desenhar num <ulink url="kdeapi:qt/QPaintDevice"
>QPaintDevice</ulink
>. Existem três dispositivos de pintura implementados: um é o <ulink url="kdeapi:qt/QWidget"
>QWidget</ulink
> que representa um elemento gráfico no ecrã. A outra é o <ulink url="kdeapi:qt/QPrinter"
>QPrinter</ulink
> que representa uma impressora e que produz o resultado em &PostScript;. A terceira é a <ulink url="kdeapi:qt/QPicture"
>QPicture</ulink
> que guarda os comandos de desenho e que poderá gravá-los em disco e reproduzi-los depois. Uma formato possível para os comandos de desenho é a norma SVG da W3C. </para>
<para
>Como tal, é possível reaproveitar o código de desenho que você usa para mostrar num item gráfico ou para imprimir, usando as mesmas funcionalidades suportadas. Claro que, na prática, o código é usado num contexto ligeiramente diferente. Desenhar num item gráfico é quase exclusivamente feito no método <methodname
>paintEvent()</methodname
> da classe de um elemento gráfico. </para>
<programlisting
>void ElementoXPTO::paintEvent()
{
QPainter p(this);
// Configurar o pintor
// Usar o pintor
}
</programlisting>
<para
>Ao desenhar numa impressora, você terá de se certificar que usa o <methodname
>QPrinter::newPage()</methodname
> para terminar uma página e começar uma nova - algo que naturalmente não é relevante ao desenhar elementos gráficos. Também, durante a impressão, você poderá querer usar a classe de <ulink url="kdeapi:qt/QPaintDeviceMetrics"
>métricas do dispositivo</ulink
> para poder calcular as coordenadas. </para>
</simplesect>
<simplesect id="qpainter-transformations">
<title
>Transformações</title>
<para
>Por omissão, ao usar o QPainter, ele desenha no sistema de coordenadas natural do dispositivo usado. Isto significa que, se você desenhar uma linha horizontal no eixo horizontal com um tamanho de 10 unidades, ele será pintado ao longo do ecrã com um tamanho de 10 pixels. Contudo, o QPainter pode aplicar várias transformações antes de desenhar propriamente as formas e as curvas. Uma transformação por afinidade mapeia as coordenadas X e Y linearmente em X' e Y' de acordo com </para>
>A matriz 3x3 desta equação poderá ser configurada com o método <methodname
>QPainter::setWorldMatrix()</methodname
> e é do tipo <ulink url="kdeapi:qt/QWMatrix"
>QWMatrix</ulink
>. Normalmente, esta é a matriz identidade, isto é, o m11 e o m22 are one, e os outros parâmetros são zero. Existem basicamente três grupos diferentes de transformações: </para>
<itemizedlist>
<listitem
><formalpara>
<title
>Translações</title>
<para
>Estas movem todos os pontos de um objecto de uma determinada quantidade numa dada direcção. A matriz de translação poderá ser obtida, invocando o método 'm.translate(dx, dy)' para uma QWMatrix. Isto corresponde à matriz </para>
>Esta matriz aumenta ou encolhe as coordenadas de um objecto, tornando-o maior ou menor sem o distorcer. Uma transformação de escala poderá ser aplicada a uma QWMatrix se invocar o m.scale(sx, sy). Isto corresponde à matriz </para>
>Configurando um dos parâmetros como negativo, uma pessoa poderá obter um espelho do sistema de coordenadas. </para>
</listitem>
<listitem
><formalpara>
<title
>Inclinação</title>
<para
>Uma distorção do sistema de coordenadas com dois parâmetros. Uma transformação por inclinação poderá ser aplicada se chamar m.shear(sh, sv), correspondendo à matriz </para>
>Isto roda um objecto. Uma transformação por rotação poderá ser aplicada se chamar m.rotate(alfa). Lembre-se que o ângulo tem de ser dado em graus, não como um ângulo matemático! A matriz correspondente é </para>
>As transformações podem ser combinadas, multiplicando as matrizes elementares. Repare que as operações com matrizes não comutativas de um modo geral, como tal o efeito combinado de uma concatenação depende da ordem pela qual as matrizes são multiplicadas. </para>
</simplesect>
<simplesect id="qpainter-strokeattributes">
<title
>Definir os atributos dos traços</title>
<para
>O desenho das linhas, curvas e contornos dos polígonos pode ser modificado se aplicar um traço especial com o QPainter::setPen(). O argumento desta função é um objecto <ulink url="kdeapi:qt/QPen"
>QPen</ulink
>. As propriedades gravadas nele são o estilo, a cor, o estilo da junção e o estilo dos extremos. </para>
<para
>O estilo do traço é um membro do tipo enumerado <ulink url="kdeapi:qt/Qt#PenStyle-enum"
>O estilo de preenchimento dos polígonos, dos círculos e dos rectângulo poderá ser modificado se definir um pincel especial com o QPainter::setBrush(). Esta função recebe um objecto <ulink url="kdeapi:qt/QBrush"
>QBrush</ulink
> como argumento. Os pincéis podem ser construídos de quatro formas diferentes: </para>
<itemizedlist>
<listitem>
<para
>QBrush::QBrush() - Isto cria um pincel que não preenche as formas geométricas.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(BrushStyle) - Isto cria um pincel preto com um dos padrões predefinidos que são mostrados em baixo.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(const QColor &, BrushStyle) - Isto cria um pincel colorido com um dos seguinte padrões mostrados em baixo.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(const QColor &, const QPixmap) - Isto cria um pincel colorido com o padrão personalizado que você passar como segundo parâmetro.</para>
</listitem>
</itemizedlist>
<para
>Um estilo de pincel predefinido pertence ao tipo enumerado <ulink url="kdeapi:qt/Qt#BrushStyle-enum"
>Qt::BrushStyle</ulink
>. Aqui está uma imagem com todos os padrões predefinidos: </para>
>Uma outra forma de personalizar o comportamento do pincel é usando a função <methodname
>QPainter::setBrushOrigin()</methodname
>. </para>
</simplesect>
<simplesect id="qpainter-color">
<title
>Cor</title>
<para
>As cores têm um papel activo, quer a traçar as curvas, quer a preencher as formas geométricas. No Qt, as cores são representadas pela classe <ulink url="kdeapi:qt/QColor"
>QColor</ulink
>. O Qt não suporta as funcionalidades gráficas avançadas, como os perfis de cores ICC e a correcção de cores. As cores são normalmente definidas, indicando os valores das componentes vermelha, verde e azul, dado que o modelo RGB é a forma como os pixels são compostos num monitor. </para>
<para
>É também possível usar o matiz, a saturação e o valor. Esta representação HSV é a que você usa na janela de cores do Gtk, p.ex. no GIMP. Aí, o matiz corresponde ao ângulo na roda de cores, enquanto a saturação corresponde à distância ao centro do círculo. O valor pode ser escolhido com uma barra em separado. </para>
</simplesect>
<simplesect id="qpainter-paintsettings">
<title
>Outras configurações</title>
<para
>Normalmente, quando você desenha num dispositivo de pintura, os pixels que você desenho substituem os que lá estavam anteriormente. Isto significa que, se você pintar uma dada região com uma cor vermelho e pintar a mesma região com uma cor azul depois, só a cor azul ficará visível. O modelo de imagens do Qt não suporta a transparência, i.e., uma forma de misturar o fundo preenchido e os desenhos. Contudo, existe uma forma simples de combinar o desenho e o fundo com operadores booleanos. O método QPainter::setRasterOp() define o operador usado, que vem do tipo enumerado <ulink url="kdeapi:qt/Qt#RasterOp-enum"
>RasterOp</ulink
>. </para>
<para
>O valor por omissão é o CopyROP que ignora o fundo. Outra escolha normal é a XorROP. Se você pintar uma linha a preto com este operador numa imagem colorida, então a área coberta será invertida. Este efeito é usado, por exemplo, para criar as selecções tracejadas dos programas de manipulação de imagens que são conhecidas por "formigas a marchar". </para>
</simplesect>
<simplesect id="qpainter-primitives">
<title
>Desenhar primitivas gráficas</title>
<para
>Na secção seguinte iremos listar os elementos gráficos primitivos suportados pelo QPainter. A maioria deles existem em várias versões alternativas que recebem um conjunto diferente de argumentos. Por exemplo, os métodos que lidam com rectângulos normalmente recebem um <ulink url="kdeapi:qt/QRect"
>QRect</ulink
> como argumento, ou então um conjunto de quatro inteiros. </para>
<itemizedlist>
<listitem>
<para
>Desenhar um único ponto - drawPoint().</para>
</listitem>
<listitem>
<para
>Desenhar linhas - drawLine(), drawLineSegments() e drawPolyLine().</para>
</listitem>
<listitem>
<para
>Desenhar e preencher rectângulos - drawRect(), drawRoundRect(), fillRect() e eraseRect().</para>
</listitem>
<listitem>
<para
>Desenhar e preencher círculos, elipses ou partes deles - drawEllipse(), drawArc(), drawPie e drawChord().</para>
</listitem>
<listitem>
<para
>Desenhar e preencher polígonos gerais - drawPolygon().</para>
</listitem>
<listitem>
<para
>Desenhar curvas Bezier - drawQuadBezier() [drawCubicBezier no Qt 3.0].</para>
</listitem>
</itemizedlist>
</simplesect>
<simplesect id="qpainter-pixmaps">
<title
>Desenhar imagens</title>
<para
>O Qt oferece duas classes muito diferentes para representar as imagens. </para>
<para
>A <ulink url="kdeapi:qt/QPixmap"
>QPixmap</ulink
> corresponde directamente aos objectos das imagens no X11. As imagens são objectos do lado do servidor e podem - numa placa gráfica moderna - até mesmo ser gravadas directamente na memória da placa. Isto torna <emphasis
>bastante</emphasis
> eficiente a transferência de imagens para o ecrã. As imagens também funcionam como um equivalente, fora do ecrã , dos elementos gráficos - a classe QPixmap é uma subclasse da QPaintDevice, por isso você poderá desenhar nela com um QPainter. As operações elementares de desenho são normalmente aceleradas pelos dispositivos gráficos modernos. Daí, um padrão de uso normal é usar as imagens para fazer duplo-'buffering'. Isto significa que, em vez de desenhar directamente num elemento gráfico, você desenha num objecto temporário de imagem e usa a função <ulink url="kdeapi:qt/QPaintDevice#bitBlt-1"
>bitBlt</ulink
> para transferir a imagem para o elemento gráfico. Para os desenhos complexos, isto ajuda a evitar intermitências. </para>
<para
>Em contraste, os objectos <ulink url="kdeapi:qt/QImage"
>QImage</ulink
> residem do lado do cliente. A sua ênfase é na fornecer um acesso directo aos pixels da imagem. Isso torna-os úteis para manipular imagens e para coisas como a leitura e gravação em disco (o método load() da QPixmap recebe uma QImage como passo intermédio). Por outro lado, desenhar uma imagem num elemento gráfico é uma operação relativamente dispendiosa, dado que implica uma transferência para o servidor X, o que ainda poderá levar algum tempo, especialmente para imagens grandes e para servidores remotos. Dependendo da profundidade de cor, a conversão de uma QImage para uma QPixmap pode necessitar de ajuste de cores. </para>
</simplesect>
<simplesect id="qpainter-drawingtext">
<title
>Desenhar texto</title>
<para
>O texto poderá ser desenhado com uma das variantes do método QPainter::drawText(). Estas desenham uma QString quer num dado ponto, quer num dado rectângulo, usando o tipo de letra definido pelo QPainter::setFont(). Existe também um parâmetro que recebe uma combinação do tipo OU de algumas opções dos tipos enumerados <ulink url="kdeapi:qt/Qt#AlignmentFlags-enum"
>Qt::AlignmentFlags</ulink
> e <ulink url="kdeapi:qt/Qt#TextFlags-enum"
>Qt::TextFlags</ulink
> </para>
<para
>A partir da versão 3.0, o Qt toma conta da disposição completa do texto, mesmo para as línguas escritas da direita para a esquerda. </para>
<para
>Uma forma mais avançada de mostrar o texto marcado é usando a classe <ulink url="kdeapi:qt/QSimpleRichText"
>QSimpleRichText</ulink
>. Os objectos desta classe podem ser construídos com um pedaço de texto que usa um sub-conjunto das marcas de HTML, o qual é bastante rico e até fornece tabelas. O estilo do texto pode ser personalizado com uma <ulink url="kdeapi/qt/QStyleSheet"
>QStyleSheet</ulink
> (a documentação das marcas pode também ser encontrada aqui). Logo que o objecto de texto formatado tenha sido construído, pode ser desenhado num elemento gráfico ou noutro dispositivo de pintura com o método QSimpleRichText::draw(). </para>
</simplesect>
</sect1>
<sect1 id="graphics-qcanvas">
<title
>Imagens estruturadas com o QCanvas</title>
<para
>A QPainter oferece um modelo de imagens poderosos para desenhar em elementos gráficos e imagens. Contudo, também poderá ser complicadíssimo de usar. De cada vez que o seu elemento gráfico recebe um evento de desenho, ele terá de analisar o QPaintEvent::region() ou o QPaintEvent::rect() que tem de ser desenhado de novo. Aí, ele terá de configurar um QPainter e desenhar todos os objectos que se sobrepõem com essa região. Por exemplo, imagine um programa de gráficos vectoriais que permita arrastar objectos como polígonos, círculos e grupos de outros objectos. De cada vez que esses objectos se movem um pouco, o tratador para os eventos do rato do elemento gráfico irá despoletar um evento para a área toda coberta pelos objectos na sua posição antiga e na sua posição nova. Descobrir quais são as actualizações necessárias e fazê-las de forma eficiente poderá ser difícil e poderá também entrar em conflito com a estrutura orientada por objectos do código-fonte do programa. </para>
<para
>Como alternativa, o Qt contém a classe <ulink url="kdeapi:qt/QCanvas"
>QCanvas</ulink
>, na qual você coloca os objectos gráficos, como os polígonos, o texto e as imagens. Você também poderá também fornecer itens adicionais se criar uma subclasse de <ulink url="kdeapi:qt/QCanvasItem"
>QCanvasItem</ulink
> ou uma das suas subclasses especializadas. Uma área de desenho ('canvas') poderá ser mostrada no ecrã por um ou mais elementos gráficos da classe <ulink url="kdeapi:qt/QCanvas"
>QCanvasView</ulink
>, a qual você terá de criar uma subclasse para tratar das interacções com o utilizador. O Qt tratará de todas as actualizações do desenho dos objectos na área visível, quer sejam provocadas pela exposição da janela, quer pela criação ou modificação dos objectos ou ainda por qualquer outra razão. Usando o duplo-'buffering', isto poderá ser feito de uma forma eficiente e livre de intermitências. </para>
<para
>Os itens da área de desenho podem-se sobrepor uns aos outros. Neste caso, o visível depende da ordem do 'z' que pode ser atribuída pelo QCanvasItem::setZ(). Os itens podem também ser tornados visíveis ou invisíveis. Você pode também indicar um fundo a ser desenhado "por detrás" de todos os itens e uma imagem de primeiro plano. Para associar os eventos do rato com os objectos na área de desenho, existe o método QCanvas::collisions() que devolve uma lista dos itens que se sobrepõem em qualquer ponto. Aqui mostramos uma imagem de uma vista sobre a área de desenho em acção: </para>
<mediaobject>
<imageobject
><imagedata fileref="canvas.png"/></imageobject>
</mediaobject>
<para
>Aqui, a malha é desenhada no fundo. Para além disso, existe um item QCanvasText item e um QCanvasPolygon violeta. A borboleta é um QCanvasPixmap. Ele tem áreas transparentes, por isso é possível ver os itens de baixo através dele. </para>
<para
>Um tutorial sobre a utilização do QCanvas para criar jogos baseados em imagens poderá ser encontrado <ulink url="http://zez.org/article/articleview/2/1/"
>aqui</ulink
>. </para>
</sect1>
<sect1 id="graphics-qglwidget">
<title
>Gráficos 3D com o OpenGL</title>
<simplesect id="qglwidget-lowlevel">
<title
>Interface de baixo nível</title>
<para
>A norma de facto para desenhar gráficos 3D hoje em dia é o <ulink url="http://www.opengl.org"
>OpenGL</ulink
>. As implementações desta especificação vêm com o Microsoft Windows, o Mac OS X e o XFree86 e muitas vezes suportam as funcionalidades de aceleração por 'hardware' oferecidas pelas placas gráficas modernas. O OpenGL em si só lida com o desenho de uma determinada área do ecrã através de um <emphasis
>contexto GL</emphasis
> e não tem nenhuma interacção com a plataforma do ambiente </para>
<para
>O Qt fornece o item <ulink url="kdeapi:qt/QGLWidget"
>QGLWidget</ulink
> que encapsula uma janela com um contexto GL associado. Basicamente, você poderá utilizá-la se criar uma sub-classe dela e implementar de novo alguns métodos. </para>
<itemizedlist>
<listitem
><para
>Em vez de reimplementar o <methodname
>paintEvent()</methodname
> e usar o QPainter para desenhar o conteúdo do elemento, você irá sobrepor o <methodname
>paintGL()</methodname
> e usar os comandos do GL para desenhar uma cena. A <classname
>QLWidget</classname
> irá tomar conta de tornar o seu contexto GL o actual antes de invocar o <methodname
>paintGL()</methodname
> e irá remeter tudo no fim. </para
></listitem>
<listitem
><para
>O método virtual <methodname
>initializeGL()</methodname
> é invocado logo da primeira vez em que o <methodname
>resizeGL()</methodname
> ou o <methodname
>paintGL()</methodname
> são chamados. Isto pode ser usado para construir listas de visualização para os objectos e para fazer as várias inicializações. </para
></listitem>
<listitem
><para
>Em vez de reimplementar o <methodname
>resizeEvent()</methodname
>, você irá sobrepor o <methodname
>resizeGL()</methodname
>. Este poderá ser usado para definir apropriadamente o porto de visualização. </para
></listitem>
<listitem
><para
>Em vez de invocar o <methodname
>update()</methodname
> sempre que o estado da cena é mudado - por exemplo, quando você o anima com um temporizador -, você deverá invocar o <methodname
>updateGL()</methodname
>. Isto irá despoletar uma actualização. </para
></listitem>
</itemizedlist>
<para
>De um modo geral, o QGLWidget comporta-se como outro item gráfico, i.e., por exemplo, você poderá processar os eventos normais do rato como de costume, redimensionar a janela e combiná-la com outras numa dada disposição. </para>
<mediaobject>
<imageobject
><imagedata fileref="opengl.png"/></imageobject>
</mediaobject>
<para
>O Qt contém alguns exemplos de utilização do QGLWidget no seu exemplo <literal
>demo</literal
>. Pode-se encontrar uma colecção de tutoriais <ulink url="http://www.libsdl.org/opengl/intro.html"
>aqui</ulink
>, assim como mais informações e uma referência ao OpenGL, na <ulink url="http://www.opengl.org"
>página pessoal do OpenGL</ulink
>. </para>
</simplesect>
<simplesect id="qglwidget-highlevel">
<title
>Interfaces de alto-nível</title>
<para
>O OpenGL é uma interface de relativo baixo nível para gráficos 3D. Da mesma forma que o QCanvas dá ao programador uma interface de maior nível com detalhes para os objectos e as suas propriedades, existem também interfaces de alto nível para os gráficos 3D. Uma das mais conhecidas é o Open Inventor. Sendo originalmente uma tecnologia desenvolvida pela SGI, existe hoje em dia também a implementação 'open-source' que é a <ulink url="http://www.coin3d.org"
>Coin</ulink
>, complementada por uma interface para a plataforma e para o Qt chamada SoQt. </para>
<para
>O conceito básico do Open Inventor é o de uma <emphasis
>cena</emphasis
>. Uma cena poderá ser carregada a partir do disco e gravada num formato ligeiramente relacionado com o <ulink url="http://www.vrml.org"
>VRML</ulink
>. Uma cena consiste numa colecção de objectos chamados <emphasis
>nós</emphasis
>. O Inventor já contém uma rica colecção de nós reutilizáveis, como cubos, cilindros e malhas, complementados por fontes de luz, materiais, câmaras, etc. Os nós são representados por classes de C++ e podem ser combinados e herdados. </para>
<para
>Pode-se encontrar uma introdução ao Inventor <ulink url="http://www.motifzone.com/tmd/articles/OpenInventor/OpenInventor.html"
>aqui</ulink
> (de um modo geral, você poderá substituir todas as menções ao SoXt por SoQt neste artigo). </para>
</simplesect>
</sect1>
</chapter>
<chapter id="userinterface">
<title
>Interface do utilizador</title>
<sect1 id="userinterface-actionpattern">
<title
>O padrão de acções</title>
<para
></para>
</sect1>
<sect1 id="userinterface-xmlgui">
<title
>Definir os menus e as barras de ferramentas em XML</title>
<simplesect id="xmlgui-intro">
<title
>Introdução</title>
<para
>Embora o <link linkend="userinterface-actionpattern"
>padrão de acções</link
> permita encapsular as acções despoletadas pelo utilizador num objecto que possa estar "ligado" noutro sítio qualquer nos menus ou nas barras de ferramentas, ele não resolve por si só o problema de construir os próprios menus. Em particular, você terá de criar todos os menus em código de C++ e inserir explicitamente as acções por uma determinada ordem, segundo as considerações dos guias de estilo para as acções normais. Isto torna bastante difícil para os utilizadores poderem personalizar os menus ou alterar os menus ou os atalhos de teclado de acordo com as suas necessidades, sem ter de alterar o código-fonte. </para>
<para
>Este problema é resolvido através de um conjunto de classes chamado <literal
>XMLGUI</literal
>. Basicamente, isto separa as acções (codificadas em C++) da sua aparência nas barras de menu e de ferramentas (codificadas em XML). Sem modificar nenhum código-fonte, os menus podem simplesmente ser personalizados, ajustando um ficheiro de XML. Para além disso, ajuda a garantir que as acções normais (como o <menuchoice
><guimenu
>Ficheiro</guimenu
><guimenuitem
>Abrir</guimenuitem
></menuchoice
> ou o <menuchoice
><guimenu
>Ajuda</guimenu
><guimenuitem
>Acerca</guimenuitem
></menuchoice
>) aparecem nas localizações indicadas pelos guias de estilo. O XMLGUI é especialmente importante para os programas modulares, em que os itens que aparecem no menu podem resultar de vários 'plugins' ou componentes diferentes. </para>
> e, por isso, suporta a XMLGUI logo de raiz. Todas as acções criadas dentro dela terão de ter a <literal
>actionCollection()</literal
> do cliente como 'pai'. Uma chamada ao <literal
>createGUI()</literal
> irá então criar o conjunto completo de barras de menu e de ferramentas definidas no ficheiro XML da aplicação (que tem, convencionalmente, o sufixo <literal
>ui.rc</literal
>). </para>
</simplesect>
<simplesect id="xmlgui-kviewexample">
<title
>Um exemplo: Menu do KView</title>
<para
>No seguinte exemplo, iremos ver o visualizador de imagens <application
>KView</application
> do KDE. Ele tem um ficheiro <literal
>ui.rc</literal
> chamado <filename
>kviewui.rc</filename
> que é instalado com o código em <filename
>Makefile.am</filename
> </para>
<programlisting
>rcdir = $(kde_datadir)/kview
rc_DATA = kviewui.rc
</programlisting>
<para
>Aqui está um excerto do ficheiro <filename
>kviewui.rc</filename
>. Por questões de simplicidade, mostramos apenas a definição no menu <guimenu
>View</guimenu
> (Ver). </para>
<programlisting
><!DOCTYPE kpartgui>
<kpartgui name="kview">
<MenuBar>
<Menu name="view" >
<Action name="zoom50" />
<Action name="zoom100" />
<Action name="zoom200" />
<Action name="zoomMaxpect" />
<Separator/>
<Action name="fullscreen" />
</Menu>
</MenuBar>
</kpartgui>
</programlisting>
<para
>A componente correspondente da configuração em C++ é: </para>
>. O elemento exterior do ficheiro contém o nome da instância da aplicação como atributo. Poderá também conter um número de versão no formato "version=2". Isto é útil quando você lança novas versões de uma aplicação com uma estrutura de menus alterada, p.ex., com mais funcionalidades. Se você fornecer o número da versão no ficheiro <literal
>ui.rc</literal
>, o KDE certifica-se que qualquer versão personalizada do ficheiro é eliminada e passa a ser usado o novo ficheiro em alternativa. </para>
<para
>A próxima linha, a <literal
><MenuBar></literal
>, contém a declaração de um menu. Você poderá também introduzir uma quantidade qualquer de declarações de <literal
><ToolBar></literal
> para criar algumas barras de ferramentas. O menu contém um submenu com o nome "view" (ver). Este nome já está predefinido e, por isso, irá ver a versão traduzida de "View" (no caso do português, "Ver") na imagem. Se você declarar os seus próprios submenus, você terá de adicionar explicitamente o título. Por exemplo, o <application
>KView</application
> tem um submenu com o título "Image" (Imagem) que é declarado da seguinte forma: </para>
> da aplicação, para que seja usados pelos tradutores. Lembre-se que você tem de representar o marcador de aceleradores "&" na forma compatível com o XML "&amp;". </para>
<para
>Voltando ao exemplo do <application
>KView</application
>, o seu menu <guimenu
>View</guimenu
> contém um conjunto de acções personalizadas: <literal
>zoom50</literal
>, <literal
>zoom100</literal
>, <literal
>zoom200</literal
>, <literal
>zoomMaxpect</literal
> e <literal
>fullscreen</literal
>, declaradas com um elemento <literal
><Action></literal
>. O separador nas imagens corresponde ao elemento <literal
><Separator></literal
>. </para>
<para
>Você irá reparar que alguns itens do menu não têm um elemento correspondente no ficheiro XML. Estes são as <emphasis
>. Quando você cria essas acções na sua aplicação (como no exemplo em C++ acima), elas serão automaticamente introduzidas numa posição prescrita, e possivelmente com uma tecla de atalho. Você poderá procurar essas localizações no ficheiro <filename
>A primeira coisa que salta à vista é que existem muitos mais atributos que nos menus. Estes incluem: </para>
<itemizedlist>
<listitem
><para
><literal
>fullWidth</literal
>: Diz ao XMLGUI que a barra de ferramentas tem a mesma largura que a janela de topo. Dado que este está igual a "false" (falso), a barra de ferramentas só ocupa o espaço necessário e poderão ser colocadas mais barras de ferramentas na mesma linha. </para
></listitem>
<listitem
><para
><literal
>newline</literal
>: Este está relacionado com a opção acima. Se for igual a "true" (verdadeiro), a barra de ferramentas inicia uma nova linha. Caso contrário, poderá ser colocada na mesma linha que a barra anterior. </para
></listitem>
<listitem
><para
><literal
>noEdit</literal
>: Normalmente as barras de ferramentas podem ser personalizadas pelo utilizador, p.ex. na opção <menuchoice
><guimenu
>Configuração</guimenu
><guimenuitem
>Configurar as Barras de Ferramentas</guimenuitem
></menuchoice
> do <application
>Konqueror</application
>. Se esta opção for igual a "true" (verdadeiro), a barra de ferramentas em questão não fica editável. Isto é importante para as barras de ferramentas que são carregadas com itens na altura da execução, como por exemplo a barra de Favoritos do <application
>Konqueror</application
>. </para
></listitem>
<listitem
><para
><literal
>iconText</literal
>: Diz ao XMLGUI para mostrar o texto da acção ao lado do ícone. Normalmente, o texto só é mostrado como uma dica quando o cursor do rato se mantiver em cima do ícone durante algum tempo. Os valores possíveis para este atributo são o "icononly" (mostra só o ícone), "textonly" (mostra só o texto), "icontextright" (mostra o texto do lado direito do ícone) e "icontextbottom" (mostra o texto por baixo do ícone). </para
></listitem>
<listitem
><para
><literal
>hidden</literal
>: Se este valor for "true" (verdadeiro), a barra de ferramentas não fica visível inicialmente e deverá ser activada por um item de menu qualquer. </para
></listitem>
<listitem
><para
><literal
>position</literal
>: O valor por omissão para este atributo é o "top", que significa que a barra de ferramentas fica por baixo do menu. Para os programas com várias ferramentas, como os programas gráficos, poderá ser interessante substituir isto por "left" (esquerda), "right" (direita) ou "bottom" (baixo). </para
></listitem>
</itemizedlist>
</simplesect>
<simplesect id="xmlgui-dynamical">
<title
>Menus dinâmicos</title>
<para
>Obviamente, um ficheiro XML só poderá conter uma descrição estática da interface do utilizador. Normalmente, existem menus que mudam durante a execução. Por exemplo, o menu do <application
>Konqueror</application
> <guimenu
>Localização</guimenu
> contém um conjunto de itens <guimenuitem
>Abrir com XPTO</guimenuitem
> com as aplicações que são capazes de abrir um dado ficheiro com um dado tipo MIME. De cada vez que o documento apresentado muda, a lista de itens do menu muda. O XMLGUI está preparado para lidar com estes casos, usando a noção de <emphasis
>listas de acções</emphasis
>. Uma lista de acções é declarada como um itm no ficheiro XML, mas de facto consiste em várias acções que são associadas ao menu durante a execução. O exemplo acima está implementado com a seguinte declaração no ficheiro XML do <application
>Konqueror</application
>: </para>
<programlisting
><Menu name="file">
<text>&amp;Location</text>
...
<ActionList name="openwith">
...
</Menu>
</programlisting>
<para
>A função <function
>KXMLGUIClient::plugActionList()</function
> é então usada para adicionar as acções a mostrar, enquanto que a função <function
>KXMLGuiClient::unplugActionList()</function
> remove todas as acções ligadas. A rotina responsável pela actualização é semelhante à seguinte: </para>
<programlisting
>void JanelaPrincipal::actualizarAccoesAbrirCom()
{
unplugActionList("openwith"); // Nome declarado no ficheiro XML
>Lembre-se que, em contraste com as acções estáticas, as que aqui são criadas <emphasis
>não</emphasis
> têm a colecção de acções como 'mãe', como tal você é responsável por removê-las você mesmo. A forma mais simples de o fazer é usando o método <literal
>accoesAbrirCom.setAutoDelete(true)</literal
> no exemplo acima. </para>
</simplesect>
<simplesect id="xmlgui-contextmenus">
<title
>Menus de contexto</title>
<para
>Os exemplos acima só continham casos em que as barras de menu e de ferramentas de uma janela principal eram criados. Existem os casos em que os processos de criação destes repositórios estão completamente escondidos do programador por detrás da chamada do <function
>createGUI()</function
> (excepto se você tiver repositórios personalizados). Contudo, existem casos em que você deseja construir outros repositórios e preenchê-los com definições da GUI a partir do ficheiro XML. Um desses exemplos são os menus de contexto. Para obter uma referência a um menu de contexto, você terá de pedir ao criador ('factory') do cliente essa referência: </para>
> usado acima procura onde encontrar um repositório no ficheiro XML com o nome indicado. Nesse caso, uma definição possível poderia ser semelhante à seguinte: </para>
<programlisting
>...
<Menu name="menu_contexto">
<Action name="ficheiro_adicionar"/>
<Action name="ficheiro_remover"/>
</Menu>
...
</programlisting>
</simplesect>
</sect1>
<sect1 id="help">
<title
>Fornecer ajuda 'online'</title>
<para
>Tornar um programa intuitivo e fácil de usar envolve um conjunto de funcionalidades que são chamadas normalmente de ajuda 'online'. A ajuda 'online' tem vários objectivos, alguns deles em conflito: por um lado, deverá dar ao utilizador respostas à pergunta "Como é que faço uma determinada tarefa?"; por outro lado, deverá ajudar o utilizador a explorar a aplicação e a encontrar funcionalidades que ainda não conhece. É importante reconhecer que isto só poderá ser conseguido se oferecer vários níveis de ajuda: </para>
<itemizedlist>
<listitem
><para
>As dicas de ferramentas são pequenas legendas que aparecem por cima dos elementos da interface gráfica, sempre que o cursor do rato fica algum tempo sobre eles. Elas são especialmente importantes para as barras de ferramentas, onde os ícones nem sempre são suficientes para explicar o intuito de um botão. </para
></listitem>
<listitem
><para
>A ajuda o "O que é isto?" é normalmente uma explicação mais extensa e mais rica sobre um elemento gráfico ou um item do menu. É também mais complicada de usar: nas janelas, pode ser invocadas de duas formas: quer carregando em <keycombo
><keycap
>Shift</keycap
><keycap
>F1</keycap
></keycombo
> ou carregando no ponto de interrogação na barra de título (onde o suporte para a última depende do gestor de janelas). O cursor do rato irá então mudar para uma seta com um ponto de interrogação, onde a janela de ajuda aparece sempre que um elemento da interface for pressionado. A ajuda "o que é isto?" para os itens do menu é normalmente activada por um botão na barra de ferramentas que contém uma seta e um ponto de interrogação. </para
></listitem>
<listitem
><para
>O problema com esta abordagem é que o utilizador não consegue ver se um elemento contém ajudas ou não. Quando o utilizador activa o botão do ponto de interrogação e não obtém nenhuma janela de ajuda quando carregar num elemento da interface do utilizador, ele irá ficar frustrado muito depressa. </para>
<para
>A vantagem das janelas de ajuda "O Que É Isto?" oferecidas pelo Qt e pelo KDE é que elas poderão conter <ulink url="kdeapi:qt/QStyleSheet"
>texto formatado</ulink
>, i.e. poderão conter vários tipos de letra, texto em negrito e itálico, ou mesmo imagens e tabelas. </para>
>Finalmente, todos os programas deverão ter um manual. Um manual é normalmente visto no <application
>KHelpCenter</application
>, activando o menu <guimenu
>Ajuda</guimenu
>. Isto significa, que uma aplicação adicional completa aparece e distrai o utilizador do seu trabalho. Por consequência, a consulta do manual só deverá ser necessária se as outras funcionalidades como as dicas e a ajuda "o que é isto?" não forem suficientes. Obviamente, um manual tem a vantagem que não explica apenas aspectos únicos e isolados da interface do utilizador. Em vez disso, ele poderá explicar aspectos da aplicação num contexto mais amplo. Os manuais para o KDE são escritos usando a linguagem de formatação <ulink url="http://i18n.kde.org"
>DocBook</ulink
>. </para
></listitem>
</itemizedlist>
<para
>Do ponto de vista do programador, o Qt oferece uma API fácil de usar para a ajuda 'online'. Para atribuir uma dica a um dado elemento gráfico, use a classe <ulink url="kdeapi:qt/QToolTip"
>QToolTip</ulink
>. </para>
<programlisting
>QToolTip::add(e, i18n("Este elemento faz uma dada tarefa."))
</programlisting>
<para
>Se as barras de menu e as barras de ferramentas forem criadas com o <ulink url="actionpattern.html"
>. Para mostrar o manual da sua aplicação, basta usar </para>
<programlisting
>kapp->invokeHelp()
</programlisting>
<para
>Isto mostra a primeira página com o índice analítico. Quando você quer mostrar apenas uma dada secção do manual, você poderá dar um argumento adicional ao <function
>invokeHelp()</function
> que indica a 'âncora' para a qual o navegador irá saltar. </para>
</sect1>
</chapter>
<chapter id="components">
<title
>Componentes e serviços</title>
<sect1 id="components-services">
<title
>Serviços do KDE</title>
<simplesect id="services-whatarekdeservices">
<title
>O que são os serviços do KDE?</title>
<para
>A noção de um <emphasis
>serviço</emphasis
> é um conceito central da arquitectura modular do KDE. Não existe nenhuma implementação técnica restrita associada a este termo - os serviço podem ser 'plugins' sob a forma de bibliotecas dinâmicas ou podem ser programas controlados através de <ulink url="dcop.html"
>DCOP</ulink
>. Quando alegar ter um determinado <emphasis
>tipo de serviço</emphasis
>, um serviço promete implementar certas APIs ou funcionalidades. Em termos de C++, pode-se pensar num tipo de serviço como uma classe abstracta e num serviço como uma implementação dessa interface. </para>
<para
>A vantagem desta separação é clara: Uma aplicação que use um tipo de serviço não tem de conhecer as possíveis implementações od mesmo. Só usa as APIs associadas ao tipo de serviço. Desta forma, o serviço usado pode ser alterado sem afectar a aplicação. Da mesma forma, o utilizador pode configurar os serviços que ele prefere para certas funcionalidades. </para>
<para
>Alguns exemplos: </para>
<itemizedlist>
<listitem
><para
>O motor de desenho de HTML no <application
>Konqueror</application
> é uma componente embebida que implementa os tipos de serviços <literal
>KParts/ReadOnlyPart</literal
> e <literal
>Browser/View</literal
>. </para
></listitem>
<listitem
><para
>No HEAD do <application
>KDevelop</application
>, a maioria da funcionalidade está incorporada em 'plugins' com o tipo de serviço <literal
>KDevelop/Part</literal
>. No arranque, todos os serviços deste tipo são carregados, de modo a que você possa extender o IDE de uma forma muito flexível. </para
></listitem>
<listitem
><para
>Na vista em ícones, o <application
>Konqueror</application
> mostra - se estiver activo - as miniaturas das imagens, páginas HTML, PDF e ficheiros de texto. Esta capacidade pode ser extendida. Se você quiser que ela mostre imagens de antevisão dos seus próprios ficheiros de dados com algum tipo MIME, você poderá implementar um serviço do tipo <classname
>ThumbCreator</classname
>. </para
></listitem>
</itemizedlist>
<para
>Obviamente, um serviço não é só caracterizado pelos tipos de serviços que implementa, mas também em algumas <emphasis
>propriedades</emphasis
>. Por exemplo, um ThumbCreator não só alega que implementa a classe de C++ com o tipo <classname
>ThumbCreator</classname
>, mas também tem uma lista de tipos MIME pelos quais é responsável. De forma semelhante, as componentes do KDevelop têm a a linguagem de programação que suportam como uma propriedades. Quando uma aplicação pede um tipo de serviço, pode também listar as restrições nas propriedades do serviço. No exemplo acima, quando o KDevelop carrega os 'plugins' de um projecto Java, ele só pede os 'plugins' que tenham o Java como propriedade da linguagem de programação. Para esse fim, o KDE contém um <emphasis
>trader</emphasis
> (mediador) semelhante ao do CORBA com uma linguagem de pesquisa complexa. </para>
</simplesect>
<simplesect id="services-definingservicetypes">
<title
>Definir tipos de serviço</title>
<para
>Os novos tipos de serviços são adicionados ao instalar uma descrição dos mesmos em <filename
>Para além dos itens normais, este exemplo demonstra como é que você declara que um serviço tem determinadas propriedades. Cada definição de propriedades corresponde a um grupo <literal
>[PropertyDef::name]</literal
> no ficheiro de configuração. Neste grupo, o item <literal
>Type</literal
> define o tipo da propriedades. Os tipos possíveis são todos os que conseguem ser registados num <ulink url="kdeapi:qt/QVariant"
>. Este contém o nome da biblioteca do 'libtool' (sem a extensão <literal
>.la</literal
>). Também corrige (com o prefixo <literal
>init_</literal
> anexado) o nome do símbolo exportado na biblioteca que devolve uma 'factory' (fábrica) de objectos. Para o exemplo acima, a biblioteca deve conter a seguinte função: </para>
<programlisting
>extern "C" {
void *init_libkdevdoxygen()
{
return new DoxygenFactory;
}
};
</programlisting>
<para
>O tipo da classe da 'factory' <classname
>DoxygenFactory</classname
> depende do tipo de serviço específico que este serviço implementa. No nosso exemplo de um 'plugin' do KDevelop, a 'factory' deverá ser uma <classname
>A partir deste ponto, o procedimento posterior depende de novo do tipo de serviço. Para os 'plugins' genéricos, você cria objectos com o método <ulink url="kdeapi:tdecore/KLibFactory.html#ref3"
>. Para as KParts, você precisa de converter o ponteiro da 'factory' para a classe mais específica KParts::Factory e usar o seu método create(): </para>
cout << "O serviço na implementa a fábrica correcta" << endl;
}
</programlisting>
</simplesect>
<simplesect id="services-definingdcopservices">
<title
>Definir serviços de DCOP</title>
<para
>Um serviço de DCOP é normalmente implementado como um programa que é iniciado sempre que é necessário. Ele entra então em ciclo e fica à espera de ligações do DCOP. O programa poderá ser interactivo, mas também poderá correr completamente, ou durante parte do seu tempo de vida, como um servidor em segundo plano, sem que o utilizador repare nele. Um exemplo destes servidores é o <literal
>, que implementa a interacção com o utilizador, como a janela de progresso para a biblioteca TDEIO. A vantagem de um servidor centralizado deste no contexto em questão é que p.ex., pode-se mostrar o progresso da transferência para vários ficheiros diferentes numa só uma janela, mesmo que estas transferências tenham sido iniciadas a partir de aplicações diferentes. </para>
>Um serviço de DCOP é definido de forma diferente de um serviço de uma biblioteca dinâmica. Obviamente, não diz respeito a uma biblioteca, mas sim a um executável. Para além disso, os serviços de DCOP não indicam uma linha ServiceType, porque normalmente eles são iniciados pelo nomes deles. Como propriedades adicionais, contêm duas linhas: </para>
<para
>O <literal
>X-DCOP-ServiceType</literal
> define a forma como o serviço é iniciado. O valor <literal
> diz que o serviço não poderá ser iniciado mais do que uma vez. Isto significa que, se você tentar iniciar este serviço (p.ex., através do <ulink url="kdeapi:tdecore/TDEApplication.html#startServiceByName"
>, o KDE irá descobrir se já está registado no DCOP e usa o serviço em execução. Se ainda não estiver registado, o KDE irá iniciá-lo e esperar até que esteja registado. Desta forma, você poderá enviar imediatamente chamadas de DCOP para o serviço. Nesse caso, o serviço deverá ser implementado como uma <ulink url="kdeapi:tdecore/KUniqueApplication.html"
> diz que poderão coexistir várias instâncias do serviço, por isso todas as tentativas de iniciar o serviço irão criar outro processo. Como última possibilidade, o valor <literal
>None</literal
> poderá ser usado. Neste caso, o arranque do serviço não irá esperar até ter sido registado com o DCOP. </para>
> deverá normalmente ser igual a 'false' (falso). Caso contrário, quando o programa for iniciado, a barra de tarefas irá mostrar uma notificação de arranque ou, dependo da configuração do utilizador, o cursor irá mudar. </para>
>Repare que o exemplo de uma chamada de DCOP indicado aqui utiliza a codificação explícita dos argumentos. Nornalmente, você iria usar um 'stub' (uma classe de adaptação) gerado pelo 'dcopidl2cpp', por ser muito mais simples e menos sujeito a erros. </para>
<para
>No exemplo aqui dado, o serviço foi iniciado "por nome", i.e., o primeiro argumento do <function
>Todas estas chamadas recebem uma lista de URLs como segundo argumento, o qual é dado ao serviço na linha de comandos. O terceiro argumento é um ponteiro para uma <classname
>QString</classname
>. Se o início do serviço falhar, este argumento fica igual à mensagem de erro traduzida. </para>
</simplesect>
</sect1>
<sect1 id="components-mime">
<title
>Tipos MIME</title>
<simplesect id="mime-whataremimetypes">
<title
>O que são tipos MIME?</title>
<para
>Os tipos MIME são usados para descrever o tipo de conteúdo dos ficheiros ou dos blocos de dados. Originalmente, foram introduzidos para permitir o envio de imagens, ficheiros de som, etc., por e-mail (o MIME significa "Multipurpose Internet Mail Extensions" - Extensões Multi-Uso de Correio pela Internet). Depois, este sistema foi também usado pelos navegadores Web para saber como apresentar os dados enviados por um servidor Web para o utilizador. Por exemplo, uma página de HTML tem um tipo MIME "text/html", um ficheiro PostScript tem o tipo "application/postscript". No KDE, este conceito é usado em vários sítios: </para>
<itemizedlist>
<listitem
><para
>Na vista em ícones do <application
>Konqueror</application
>, os ficheiros são representados por ícones. Cada tipo MIME tem um dado ícone associado, que é aqui apresentado. </para
></listitem>
<listitem
><para
>Quando você carrega no ícone de um ficheiro ou no seu nome no <application
>Konqueror</application
>, o ficheiro tanto pode ser aberto num visualizador incorporado como numa aplicação associada ao tipo de ficheiro. </para
></listitem>
<listitem
><para
>Quando você arrasta e larga alguns dados de uma aplicação noutra (ou dentro da mesma aplicação), o destino dos dados pode optar por aceitar apenas alguns tipos de dados. Para além disso, ele irá lidar com os dados de imagens de forma diferente dos dados textuais. </para
></listitem>
<listitem
><para
>Os dados na área de transferência têm um tipo MIME. Tradicionalmente, os programas do X só lidam com imagens ou texto, mas com o Qt, não existem restrições ao tipo de dados. </para
></listitem>
</itemizedlist>
<para
>Nos exemplos acima, é óbvio que o tratamento do MIME é uma questão complexa. Primeiro, é necessário estabelecer um mapeamento dos nomes dos ficheiros para os tipos MIME. O KDE vai mais além, permitindo até que o conteúdo dos ficheiros seja mapeado em tipos MIME, para os casos em que o nome do ficheiro não está disponível. Segundo, é necessário mapear os tipos MIME nas aplicações ou nas bibliotecas que podem ver ou editar os ficheiros de um determinado tipo ou ainda criar uma imagem em miniatura deles. </para>
<para
>Existe uma variedade de APIs para descobrir o tipo MIME dos dados ou ficheiros. De um modo geral, existe um certo compromisso de velocidade/fiabilidade que terá de fazer. Você poderá descobrir o tipo de um ficheiro, examinando apenas o nome do seu ficheiro (i.e., na maioria dos casos, pela sua extensão). Por exemplo, um ficheiro <filename
>xpto.jpg</filename
> é normalmente um "image/jpeg". Nos casos em que a extensão não existe, isto não é seguro, e você terá de ver o conteúdo do ficheiro. Isto é obviamente mais lento, em particular para os ficheiros que terão de ser obtidos via HTTP em primeiro lugar. O método baseado no conteúdo baseia-se no ficheiro <filename
> e, como tal, poderá ser difícil de extender. Mas, de um modo geral, a informação do tipo MIME poderá ser disponibilizada de forma simples ao sistema se instalar um ficheiro <literal
>.desktop</literal
>, e está disponível de forma eficiente e conveniente através das bibliotecas do KDE. </para>
</simplesect>
<simplesect id="mime-definingmimetypes">
<title
>Definir os tipos MIME</title>
<para
>Vamos então definir um tipo <literal
>"application/x-xpto"</literal
> para o nosso programa <application
>programaXpto</application
>. Para tal, você terá de criar um ficheiro <filename
>O método mais rápido para determinar o tipo de um ficheiro é o <function
>KMimeType::findByURL()</function
>. Isto procura pelo texto no URL e, na maioria dos casos, determina o tipo a partir da extensão. Para certos protocolos (p.ex., 'http', 'man', 'info'), este mecanismo não é usado. Por exemplos, os 'scripts' CGI nos servidores Web escritos em Perl normalmente têm a extensão <literal
>.pl</literal
>, o que iria corresponder a um tipo <literal
>"text/x-perl"</literal
>. Todavia, o ficheiro devolvido pelo servidor é o resultado deste 'script', que é normalmente HTML. Para esse caso, o <function
>KMimeType::findByURL()</function
> devolve o tipo MIME <literal
>"application/octet-stream"</literal
> (disponível através do <function
>KMimeType::defaultMimeType()</function
>), que indica uma falha da descoberta do tipo. </para>
<programlisting
>KMimeType::Ptr tipo = KMimeType::findByURL("/home/ze/xpto.jpg");
if (tipo->name() == KMimeType::defaultMimeType())
cout << "Não foi possível descobrir o tipo" << endl;
>Você poderá querer determinar o tipo MIME a partir do conteúdo do ficheiro em vez de ser pelo seu nome. Isto é mais fiável, mas também é mais lento, dado que implica ler uma parte do ficheiro. Isto é feito com a classe <ulink url="kdeapi:tdeio/KMimeMagic.html"
>Obviamente, até mesmo o KMimeMagic só é capaz de determinar o tipo do ficheiro para o conteúdo de um ficheiro local. Para os ficheiros remotos, existe uma outra possibilidade: </para>
>Isto inicia uma tarefa do TDEIO para obter uma parte do ficheiro e analisá-la. Lembre-se que esta função é talvez bastante lenta e bloqueia o programa. Normalmente, você só irá querer usar isto se o <function
>Por outro lado, se você não quiser bloquear a sua aplicação, você poderá iniciar explicitamente a tarefa do TDEIO e ligar-se a um dos seus 'signals': </para>
>Mapear um tipo MIME a uma aplicação ou serviço</title>
<para
>Quando uma aplicação é instalada, ela coloca um ficheiro <literal
>.desktop</literal
> que contém uma lista dos tipos MIME que esta aplicação pode carregar. De forma semelhante, os componentes como as KParts disponibilizam esta informação através dos ficheiros <literal
>.desktop</literal
> dos serviços. Por isso, de um modo geral, existem vários programas e componentes que podem processar um dado tipo MIME. Você poderá obter uma dessas listas a partir da classe <classname
>Na maioria dos casos, você não está interessado na lista de todas as ofertas de serviços para uma dada combinação de tipo MIME e tipo de serviço. Existe uma função de conveniência que lhe dá apenas a oferta de serviço com a maior preferência: </para>
>Executar um URL. Isto procura pelo tipo do URL e inicia o programa preferido do utilizador associado a este tipo. </para>
<programlisting
>KURL url("http://dot.kde.org");
new KRun(url);
</programlisting>
</simplesect>
</sect1>
<sect1 id="nettransparency">
<title
>Transparência na rede</title>
<simplesect id="nettransparency-intro">
<title
>Introdução</title>
<para
>Na era na World Wide Web, é de uma importância essencial que as aplicações possam aceder aos recursos na Internet: elas deverão ser capazes de obter os ficheiros a partir de um servidor Web, gravar os ficheiros num servidor FTP ou ler as mensagens de e-mail de um servidor Web. Normalmente, a capacidade de aceder aos ficheiros, independentemente da sua localização é chamada de <emphasis
>transparência na rede</emphasis
>. </para>
<para
>No passado, foram implementadas aproximações diferentes para estes objectivos. O sistema de ficheiros antigo NFS é uma tentativa de implementar a transparência de rede ao nível da API do POSIX. Embora esta aproximação funcione bastante bem nas redes locais e próximas, não é escalável para os recursos cujo acesso não seja fiável ou seja lento. Aqui, a <emphasis
>assincronização</emphasis
> é importante. Enquanto você está à espera do seu navegador Web para transferir uma página, a interface do utilizador não deverá bloquear. Da mesma forma, o início do desenho da página não deverá começar somente quando a página estiver disponível por completo mas sim actualizar-se regulamente à medida que os dados vão chegando. </para>
> de E/S. Uma tarefa pode copiar ou remover ficheiros, entre outras coisas. Logo que uma tarefa seja inicia, ela fica em segundo plano e não bloqueia a aplicação. Todas as comunicações da tarefa para a aplicação - como a entrega dos dados ou a informação de progresso - é feita de forma integrada com o ciclo de eventos do Qt. </para>
<para
>A operação em segundo-plano é conseguida com o arranque de <emphasis
>ioslaves</emphasis
> para efectuar certas tarefas. Os 'ioslaves' são iniciados como processos separados e comunicam através de 'sockets' do domínio UNIX. Desta forma, não é necessário nenhum suporte de multi-tarefa e os 'slaves' instáveis não poderão estoirar as aplicações que os usam. </para>
<para
>As localizações dos ficheiros são indicadas pelos URLs que são usados em grande escala. Mas, no KDE, os URLs não só se expandem à gama de ficheiros endereçáveis para além do sistema de ficheiros local. Também funciona na direcção oposta - p.ex. você poderá navegar nos pacotes TAR. Isto é conseguido com os URLs aninhados. Por exemplo, um ficheiro num pacote TAR num servidor HTTP poderá ter o URL </para>
>Na maioria dos casos, as tarefas são criadas ao invocar as funções no espaço de nomes do TDEIO. Estas funções recebem um ou dois URLs como argumentos e possivelmente outros parâmetros necessários. Quando a tarefa terminar, ela emite o 'signal' <literal
>. Depois de este 'signal' ter sido emitido, a tarefa elimina-se a si própria. Deste modo, um caso de uso típico poderia ser semelhante ao seguinte: </para>
>Descobre certas informações sobre o ficheiro, como o tamanho, a data de modificação e as permissões. A informação pode ser obtida a partir do TDEIO::StatJob::statResult() depois de a tarefa terminar. </para
>Tanto as tarefas TDEIO::stat() como a TDEIO::listDir() devolvem os seus resultados como um tipo UDSEntry e UDSEntryList respectivamente. A última está definida como sendo um QValueList<UDSEntry>. O acrónimo UDS significa "Universal directory service" (serviço de directório universal). O princípio está em que o item da directoria só contém a informação que um 'ioslave' poderá fornecer, nada mais. Por exemplo, o 'slave' de HTTP não fornece nenhuma informação sobre as permissões de acesso ou os donos dos ficheiros. Em vez disso, uma UDSEntry é uma lista de UDSAtoms. Cada átomo contém um item informativo específico. Ele consiste num tipo armazenado no 'm_uds' e num valor inteiro no 'm_long' ou um valor de texto no 'm_str', dependendo do tipo. </para>
>Os seguintes tipos estão actualmente definidos: </para>
<itemizedlist>
<listitem
><para
>UDS_SIZE (inteiro) - O tamanho do ficheiro. </para
></listitem>
<listitem
><para
>UDS_USER (texto) - O utilizador que possui o ficheiro. </para
></listitem>
<listitem
><para
>UDS_GROUP (texto) - O grupo que possui o ficheiro. </para
></listitem>
<listitem
><para
>UDS_NAME (texto) - O nome do ficheiro. </para
></listitem>
<listitem
><para
>UDS_ACCESS (inteiro) - As permissões sobre o ficheiro, como é guardado p.ex. pela função da 'libc' stat() no campo 'st_mode'. </para
></listitem>
<listitem
><para
>UDS_FILE_TYPE (inteiro) - O tipo do ficheiro, tal como é, p.ex., registado pelo stat() no campo 'st_mode'. Como tal, você poderá usar as macros normais da 'libc' como a S_ISDIR para testar este valor .Repare que os dados fornecidos pelos 'ioslaves' correspondem ao stat(), não ao lstat(), i.e., no caso das ligações simbólicas, o tipo aqui mencionado é o tipo do ficheiro apontado pela ligação, não a ligação em si. </para
></listitem>
<listitem
><para
>UDS_LINK_DEST (texto) - No caso de uma ligação simbólica, o nome do ficheiro para onde esta ligação aponta. </para
></listitem>
<listitem
><para
>UDS_MODIFICATION_TIME (inteiro) - A hora (como no tipo 'time_t') em que o ficheiro foi modificado da última vez, como é p.ex. registado pelo stat() no campo 'st_mtime'. </para
></listitem>
<listitem
><para
>UDS_ACCESS_TIME (inteiro) - A hora (como no tipo 'time_t') em que o ficheiro foi acedido da última vez, como é p.ex. registado pelo stat() no campo 'st_atime'. </para
></listitem>
<listitem
><para
>UDS_CREATION_TIME (inteiro) - A hora (como no tipo 'time_t') em que o ficheiro foi criado, como é p.ex. registado pelo stat() no campo 'st_ctime'. </para
></listitem>
<listitem
><para
>UDS_URL (texto) - Fornece um URL do ficheiro, se não for simplesmente a concatenação do URL da directoria e o nome do ficheiro. </para
></listitem>
<listitem
><para
>UDS_MIME_TYPE (texto) - tipo MIME do ficheiro </para
></listitem>
<listitem
><para
>UDS_GUESSED_MIME_TYPE (texto) - o tipo MIME do ficheiro, tal como é determinado pelo 'slave'. A diferença em relação ao tipo anterior é que o que é fornecido aqui não deverá ser considerado fiável (porque determiná-lo de forma fiável será demasiado dispendioso). Por exemplo, a classe KRun verifica explicitamente o tipo MIME se não tiver nenhuma informação fiável. </para
></listitem>
</itemizedlist>
<para
>Ainda que a forma de armazenar as informações sobre os ficheiros num <classname
>UDSEntry</classname
> seja flexível e prático do ponto de vista dos 'ioslaves', é uma confusão para ser usado pelo programador da aplicação. Por exemplo, para saber qual é o tipo MIME do ficheiro, você teria de iterar por todos os átomos e testar se o <literal
>m_uds</literal
> era o <literal
>UDS_MIME_TYPE</literal
>. Felizmente, existe uma API que é bastante mais simples de usar: a classe <classname
>Normalmente, a API síncrona do TDEIO é demasiado complexa de usar e, como tal, a implementação de uma assincronização completa não é uma prioridade. Por exemplo, num programa que só consiga lidar com um ficheiro de cada vez, não há muito a fazer enquanto o programa está a transferir um ficheiro, de qualquer forma. Para esses casos simples, existe uma API muito mais simples sob a forma de funções estáticas no TDEIO::NetAccess. Por exemplo, para poder copiar um ficheiro, use </para>
>A função irá regressar depois de o processo de cópia ter terminado por completo. De qualquer forma, este método fornece uma janela de progresso e certifica-se que os processos da aplicação actualizam os eventos. </para>
<para
>Uma combinação particularmente interessante de funções é a <function
>download()</function
> em conjunto com a <function
>removeTempFile()</function
>. A primeira obtém um ficheiro a partir de um dado URL e guarda-o num ficheiro temporário com um nome único. O nome é guardado no segundo argumento. <emphasis
>Se</emphasis
> o URL for local, o ficheiro não é transferido e, em vez disso, o segundo argumento passa a ser o nome do ficheiro local. A função <function
>removeTempFile()</function
> apaga o ficheiro indicado pelo seu argumento, se o ficheiro for o resultado de uma transferência anterior. Se não for o caso, não faz nada. Desta forma, uma forma muito simples de carregar os ficheiros, independentemente da sua localização é o pedaço de código a seguir: </para>
>Como pode ser visto em cima, a interface para as tarefas de E/S é bastante abstracta e não considera nenhuma troca de informação entre a aplicação e o 'IO slave', o qual é específico do protocolo. Isto nem sempre é apropriado. Por exemplo, você poderá querer dar certos parâmetros ao 'slave' de HTTP para controlar o comportamento da sua 'cache' ou para enviar um conjunto de 'cookies' com o pedido. Para esse fim, o conceito de meta-dados foi introduzido. Quando for criada uma tarefa, você poderá configurá-la se lhe adicionar meta-dados. Cada item de meta-dados consiste num par chave/valor. Por exemplo, para evitar que o 'slave' de HTTP carregue a página Web da sua 'cache', você pode usar: </para>
>A mesma técnica é usada na outra direcção, i.e., para a comunicação do 'slave' para a aplicação. O método <function
>Job::queryMetaData()</function
> pede o valor de uma certa chave indicada pelo 'slave'. Para o 'slave' de HTTP, um desses exemplos é a chave <literal
>"modified"</literal
> (modificado), que contém uma (representação em texto da) data em que a página Web foi modificada da última vez. Um exemplo de como você pode usar isto é o seguinte: </para>
>Ao usar a API do TDEIO, você normalmente não tem de lidar com os detalhes de arranque dos 'IO slaves' e da comunicação com eles. O caso de uso normal é iniciar uma tarefa e, com alguns parâmetros, tratar os 'signals' que esta emite. </para>
>Nos bastidores, o cenário é bastante mais complicado. Quando você cria uma tarefa, esta é posta numa fila. Quando a aplicação regressa ao ciclo de eventos, o TDEIO reserva processos de 'slaves' para as tarefas na fila. Para as primeiras tarefas que sejam iniciadas, isto é trivial: um 'IO slave' para o protocolo corresponde é iniciado. Contudo, depois de a tarefa (como a transferência de um servidor de HTTP) ter terminado, não é imediatamente morta. Em vez disso, é colocada num grupo de 'slaves' inactivos e é morta ao fim de um dado período de inactividade (normalmente 3 minutos). Se surgir um novo pedido para o mesmo protocolo e máquina, o 'slave' é reutilizado. A vantagem óbvia é que, para uma série de tarefas para a mesma máquina, o custo da criação de novos processos e da passagem por um processo de autenticação é poupado. </para>
>Claro que a reutilização só é possível quando o 'slave' existente já tiver terminado a sua tarefa anterior. Sempre que chega um novo pedido enquanto um 'slave' existente está ainda a correr, é iniciado um novo processo para ser utilizado. Na utilização da API nos exemplos acima, não existe nenhuma limitação para criar novos processos de 'slaves': se você iniciar uma série consecutiva de transferências para 20 ficheiros, então o TDEIO irá iniciar 20 processos de 'slaves'. Este esquema de atribuição de 'slaves' a tarefas é chamado de <emphasis
>. Não é sempre o esquema mais apropriado, dado que poderá necessitar de bastante memória e poderá colocar uma carga elevada tanto no cliente como no servidor. </para>
<para
>Por isso, existe uma forma diferente. Você poderá <emphasis
>escalonar</emphasis
> as tarefas. Se o fizer, somente um número limitado (de momento 3) de processos de 'slaves' para um dado protocolo serão criados. Se você criar mais tarefas que isso, elas serão colocadas numa fila e são processadas quando um processo de um 'slave' ficar inactivo. Isto é feito da seguinte forma: </para>
>. Por exemplo, no caso do 'slave' de IMAP, não faz nenhum sentido lançar vários processos para o mesmo servidor. Só uma ligação de IMAP de cada vez é que deverá ser permitida. Neste caso, a aplicação deverá lidar explicitamente com a noção de um 'slave'. Terá de libertar um 'slave' para uma determinada ligação e então atribuir todas as tarefas que deverão ir pela mesma ligação ao mesmo 'slave'. Isto poderá ser conseguido facilmente com a utilização do TDEIO::Scheduler: </para>
>Você só poderá desligar o 'slave' depois de todas as tarefas atribuídas a ela terem terminado de forma garantida. </para>
</simplesect>
<simplesect id="nettransparency-definingslaves">
<title
>Definir um 'ioslave'</title>
<para
>Na parte seguinte iremos discutir como é que você poderá adicionar um novo 'ioslave' ao sistema. Em analogia aos serviços, os 'ioslaves' novos são publicados no sistema, instalando para tal um pequeno ficheiro de configuração. O seguinte excerto do Makefile.am instala o protocolo FTP: </para>
<programlisting
>protocoldir = $(kde_servicesdir)
protocol_DATA = ftp.protocol
EXTRA_DIST = $(mime_DATA)
</programlisting>
<para
>O conteúdo do ficheiro 'ftp.protocol' é o seguinte: </para>
> é iniciado, o qual por sua vez arranca esta biblioteca no seu espaço de endereçamento. Por isso, na prática, você poderá pensar no 'slave' em execução como um processo separado, ainda que seja implementado como uma biblioteca. A vantagem deste mecanismo é que poupa bastante memória e reduz o tempo necessário pelo editor de ligações durante a execução. </para>
<para
>As linhas "input" e "output" não são usadas de momento. </para>
> definem quais as capacidades que o 'slave' tem. De um modo geral, as funcionalidades que um 'slave' tem de implementar são muito mais simples do que as funcionalidades que a API do TDEIO oferece à aplicação. A razão para tal é que as tarefas complexas são escalonadas para um conjunto de sub-tarefas. Por exemplo, para poder listar uma directoria recursivamente, terá de ser iniciada uma tarefa para a directoria de topo. Depois, para cada subdirectoria indicada, são criadas novas sub-tarefas. Um escalonador no TDEIO certifica-se que não estão demasiadas tarefas activas ao mesmo tempo. De forma semelhante, para poder copiar um ficheiro num protocolo que não suporte a cópia directa (como o protocolo <literal
> precisa de publicar as acções que o seu 'slave' suporta. </para>
<para
>Dado que os 'slaves' são carregados como bibliotecas dinâmicas, mas constituem em si programas autónomos, a sua plataforma de código parece ligeiramente diferente dos 'plugins' normais das bibliotecas dinâmicas. A função que é invocada para iniciar o 'slave' chama-se <function
>kdemain()</function
>. Esta função faz algumas inicializações e vai então para o ciclo de eventos, onde fica à espera de pedidos da aplicação que a usa. Isto parece-se com o seguinte: </para>
<programlisting
>extern "C" { int kdemain(int argc, char **argv); }
>. Para essas operações, o TDEIO determina automaticamente se elas são suportadas ou não (i.e., se a implementação por omissão devolve um erro). </para>
>Todas estas implementações deverão terminar com uma de duas chamadas: Se a operação foi bem sucedida, deverão invocar o <literal
>finished()</literal
>. Se ocorreu um erro, o <literal
>error()</literal
> deverá ser invocado com um código de erro como primeiro argumento e um texto no segundo. Os códigos de erro possíveis estão listados no tipo enumerado <type
> para parametrizar a mensagem de erro para o utilizador. </para>
<para
>Para os 'slaves' que correspondem aos protocolos de rede, poderá ser interessante reimplementar o método <function
>SlaveBase::setHost()</function
>. Isto é chamado para indicar ao processo do 'slave' qual a máquina e o porto, assim como o utilizador e a senha a usar. De um modo geral, os meta-dados definidos pela aplicação poderão ser questionados pelo <function
>SlaveBase::metaData()</function
>. Você poderá verificar a existência de meta-dados de uma determinada chave com o <function
>SlaveBase::hasMetaData()</function
>. </para>
</simplesect>
<simplesect id="nettransparency-communication">
<title
>Comunicar de volta à aplicação</title>
<para
>As várias acções implementadas num 'slave' precisam de uma forma qualquer para comunicar de volta para a aplicação, usando o processo do 'slave': </para>
<itemizedlist>
<listitem
><para
>O <function
>get()</function
> envia blocos de dados. Isto é feito com o <function
>data()</function
>, que recebe um <classname
>QByteArray</classname
> como argumento. Claro que não precisa de enviar todos os dados de uma vez. Se enviar um ficheiro grande, invoque o <function
>data()</function
> com blocos de dados mais pequenos, de modo a que a aplicação os processe. Invoque o <function
>finished()</function
> quando a transferência terminar. </para
></listitem>
<listitem
><para
>O <function
>listDir()</function
> devolve informações sobre os itens de uma directoria. Para esse fim, chame o <function
>, o qual será discutido em baixo. Use o <function
>statEntry()</function
> para enviar um desses itens para a aplicação. </para
></listitem>
<listitem
><para
>O <function
>mimetype()</function
> chama o <function
>mimeType()</function
> com um argumento de texto. </para
></listitem>
<listitem
><para
>O <function
>get()</function
> e o <function
>copy()</function
> podem querer fornecer informações de progresso. Isto é feito com os métodos <function
>totalSize()</function
>, <function
>processedSize()</function
>, <function
>speed()</function
>. O tamanho total e o tamanho processado são indicados em 'bytes', enquanto que a velocidade é dada em 'bytes' por segundo. </para
></listitem>
<listitem
><para
>Você poderá enviar pares arbitrários de chaves/valores de meta-dados com o <function
>setMetaData()</function
>. </para
></listitem>
</itemizedlist>
</simplesect>
<simplesect id="nettransparency-interacting">
<title
>Interagir com o utilizador</title>
<para
>Algumas das vezes, um 'slave' precisa de interagir com o utilizador. Os exemplos incluem as mensagens de informações, as janela de autenticação e as janelas de confirmação, sempre que um ficheiro está prestes a ser sobreposto. </para>
<itemizedlist>
<listitem
><para
><function
>infoMessage()</function
> - Isto é para fins de informação, como a mensagem "A obter dados de <máquina>" do 'slave' de HTTP, o qual é mostrado normalmente na barra de estado do programa. Do lado da aplicação, este método corresponde ao 'signal' <function
>. Se ainda estiver aberta uma mensagem de uma invocação anterior do warning() do mesmo processo-filho, nada acontece. </para
></listitem>
<listitem
><para
><function
>messageBox()</function
> - Este é mais rico que o método anterior. Permite-lhe abrir uma mensagem com um texto e um título, assim como alguns botões. Veja o tipo enumerado <type
>SlaveBase::MessageBoxType</type
> para mais referências. </para
></listitem>
<listitem
><para
><function
>openPassDlg()</function
> - Abre uma janela onde se poderá indicar o nome do utilizador e a sua senha. </para