Home
C/C++
A Hierarquia de Classes SST
 
A Hierarquia de Classes SST

Do original: The SST Class Hierarchy by Marshall Brain & Kelly Campbell


Apresentação

O conceito de hierarquia de classes é um dos de mais difícil compreensão pelos programadores, tanto iniciantes quanto experientes em C++. Embora seja simples de definir - uma hierarquia de classes é um conjunto de classes que se constrói tomando umas como base para as outras e usando os recursos de herança e funções virtuais - e simples de justificar - a principal razão para criar hierarquia de classes é reusabilidade - é bastante difícil sair do plano conceitual e atingir proficiência nesse tema, porque as hierarquias de classes tendem a ser códigos muito extensos, amplamente auto-referenciados. Pode-se levar muito tempo para compreender completamente uma hierarquia grande, especialmente se for a primeira vez que se lida com hierarquia de classes.

Comprovadamente, um dos melhores modos de aprender C++ é examinar e usar hierarquias de classes de terceiros. Dessa forma você poderá começar a entender as vantagens da herança e do polimorfismo, e você ainda terá acesso a várias técnicas de projeto que lhe serão úteis. O problema com as hierarquias existentes é que na maioria dos casos são muito complexas. Por exemplo, a hierarquia de classes MFC contém centenas de classes, milhares de funções membro e dezenas de milhares de linhas de código. Uma pessoa poderia levar anos para compreender completamente todos os aspectos dessa hierarquia.

O objetivo desse artigo é apresentar um exemplo de hierarquia de classes, a SST - Super Simple Toolkit. A SST é uma hierarquia de classes para interface gráfica com o usuário (GUI), que lhe permite criar uma aplicação GUI com muita facilidade. Mais ainda, você pode comparar essa hierarquia com MFC, com as classes OWL da Borland, com Turbo Vision, com a hierarquia de classes Java, etc. SST é, como o próprio nome já indica, super simples. Entretanto, dentro dessa simplicidade, contém três vantagens muito importantes:

  • Por ser simples, você pode dominar SST em um ou dois dias de estudo. Ou seja, você pode compreender completamente o uso da hierarquia, utilizá-la para criar uma aplicação simples, adicionar-lhe vários controles novos, e ainda compreender o código fonte que cria o SST em apenas alguns poucos dias.
  • SST, embora pequena, contém todas as idéias centrais que fundamentam as grandes hierarquias de classes. Ao colocar você em contacto com esses conceitos fundamentais, SST o torna capaz de entender com mais facilidade as grandes hierarquias de classes.
  • SST é extremamente portável, tendo sido compilado sob Visual C++, Borland C++, AT&T C-Front, etc. É ainda adaptável a vários outros ambientes computacionais.

SST é, portanto, um valioso instrumento de estudo para programadores C++ iniciantes.

 
Topo
Introdução a Hierarquia SST

Todas as hierarquias GUI permitem que você crie objetos GUI padrões, tais como buttons, rótulos, barras de scroll, etc. SST não é diferente: é apenas muito mais simples. SST possibilita que você crie buttons, rótulos e áreas de edição. É simples o bastante para ser usada, compreendida e extendida em apenas uns poucos dias. Adicionalmente, você ainda pode examinar o código fonte (todo o código fonte de SST é apresentado ao final desse artigo) e aprender com as técnicas de herança empregadas nessa implementação. SST segue os padrões de uso encontrados na maioria das implementações GUI.

SST é uma interface baseada em caractéres totalmente portável. Isso, embora não privilegie a beleza da interface, permite que você a execute em qualquer plataforma. Um programa típico criado com o uso da SST terá uma aparência similar ao seguinte:

Nessa janela há rótulos estáticos - Celsius Temperature,
Fahrenheit Temperature
, e 212 - uma área de entrada de dados onde o valor 100 foi digitado, e ainda dois buttons - Convert e Quit. A área de entrada de dados tem um apontador, representado pelo caracter = (sinal de igualdade). A aplicação apresentada implementa uma conversão simples de graus Celsius para Fahrenheit.

Um programa SST pode conter os seguintes objetos GUI:

  • Uma janela principal
  • Rótulos
  • Buttons
  • Áreas de entrada de dados

SST entende a seguinte semântica de teclado:

  • Deslocar o apontador com a tecla Tab ou Shift-Tab
  • Ativar buttons com a tecla Enter

Os controles de edição aceitam digitação normal e backspace.

A hierarquia SST contém 10 classes, 6 das quais agrupadas hierarquicamente, como mostrado a seguir:

As quatro restantes são classes auxiliares:
  • Event - Classe evento usada em Object::HandleEvent
  • ObjectList - Classe objeto lista usada em Window
  • Sreeen - Classe desenha tela usada em Object
  • Strclass - Uma classe string simples usada em Input

Nas seções seguintes você vai aprender como criar aplicações simples usando essas classes, e vai aprender também como funciona o código fonte dessas classes.

Topo
Criando a Aplicação SST Mais Simples

Todas as aplicações SST compartilham vários elementos fundamentais. Seja qual for o grau de simplicidade, ou de complexidade, você sempre segue as seguintes etapas em toda aplicação SST:
  • Deriva sua aplicação a partir da classe Window. Declara seus controles e membros
  • Implementa um construtor para a classe derivada, que cria e inicializa os controles internos à janela
  • Sobrescreve o membro HandleEvent da classe Window para manejar os eventos vinculados aos buttons

Usando essas etapas, a aplicação mais simples que você pode criar é uma janela com um buttom Quit. Para criar tal aplicação, você deve começar herdando a classe Window:

#include <IOSTREAM.H>
#include <STRSTREA.H>

#include "button.h"
#include "rect.h"

#include "window.h"
class App: public Window
{
public:
    App( char *s, Rect &r );
    virtual void HandleEvent( Event &event );
};

App app( "Test", Rect(1, 1, 80, 24) );

...

void main()
{
    app.Execute();
}
Note que esse código declara uma classe denominada App derivada de Window, e essa classe derivada sobrescreve o construtor e a função HandleEvent. Em seguida o programa cria uma instância dessa classe como uma variável global e passa o string Test (que será o nome da janela) e o tamanho e a posição de Window na tela. Assuma que a tela tem dimensão de 80 caractéres de largura por 24 de altura. O tamanho especificado preenche a tela. A seguir o código chama o membro Execute dentro da função main para iniciar o processo.

Você vai então criar um novo construtor para a classe App. A finalidade desse construtor é inserir controles na janela:

const int QUIT  = 100;

App::App( char *s, Rect &r ): 
    Window( s, r ), count( 0 )
{
    Insert( new Button(" Quit", Rect(50, 18, 57, 20), QUIT) );
} 
A função Insert cria uma nova instância da classe Button, passando o título do button (Quit), a localização e o tamanho, e uma identificação (ID) 100 na constante denominada QUIT. Essa identificação será usada para manejar eventos gerados pelo button.

Você deve então sobrescrever a função membro HandleEvent da classe Window para manejar eventos produzidos pelo button Quit:

void App::HandleEvent( Event &event)
{
    Window::HandleEvent( event );

    if( event.type == COMMAND )
    {
        switch( event.message )
        {
            case QUIT:
              Close();
              screen.Clear();
              break;
        }
    }
    event.type = CLEAR;
}
Esse código primeiramente chama a função HandleEvent default da classe Window, para que trate automaticamente qualquer evento já conhecido pela função. Quaisquer eventos não tratados pela função HandleEvent são então testados para verificar se são eventos COMMAND. Se forem, são testados para verificar se são originados pelo button Quit. Se forem, a aplicação é encerrada com fechamento da janela e limpeza da tela.

Se você montar essas peças e executar a aplicação, vai obter a apresentação de uma janela contendo um button Quit. Quando você pressionar a tecla Enter (para ativar o button) a aplicação será encerrada.

A maneira mais fácil de compilar essa aplicação é tomar todas as classes SST (apresentadas no final desse artigo) é colocá-las em um diretório. Coloque o código mostrado aqui em um arquivo .CPP em separado. Inclua todos os arquivos .CPP em um projeto ou MAKEFILE e compile e link-edite os arquivos. Se você estiver usando compilador Visual C++ ou Borland, será preferível criar uma aplicação text-based console.

Execute a aplicação.

SST assume que a janela de entrada trata seqüência de caractéres ANSI. Assim, se você estiver usando MS-DOS ou Windows 95, defina ANSI.SYS em seu arquivo config.sys, ou seja, adicione a especificação DEVICE=ANSI.SYS ao conteúdo do arquivo config.sys e reinicie o computador. Se você estiver usando Windows NT ou UNIX, devem ser fornecidas as versões especiais dos arquivos apropriados.

A seguir, um programa um pouco mais avançado que faz uso de dois controles disponíveis na SST (rótulos e buttons). Esse programa implementa um incrementador muito simples. Quando você ativa o button Increment, o contador é incrementado de um. Quando você ativa o button Quit, o programa se encerra. Você move o apontador entre os dois buttons pressonando a tecla Tab, e ativa o button selecionado pressionando a tecla Enter.

// Counter program

#include <IOSTREAM.H>
#include <STRSTREA.H>

#include "button.h"
#include "input.h"
#include "label.h"
#include "rect.h"
#include "window.h"

class App: public Window
{
    Label *number;
    int count;
public:
    App( char *s, Rect &r );
    virtual void HandleEvent( Event &event );
};

App app( "Test", Rect(1, 1, 80, 24) );

const int QUIT = 100;
const int COUNT = 101;

App::App( char *s, Rect &r ): 
    Window( s, r ), count( 0 )
{
    Insert( new Label("Count: ", 
        Rect(20, 5, 30, 7)) );
    number = new Label("0", 
        Rect(31, 5, 40, 7));
    Insert( number );

    Insert( new Button(" Increment", 
        Rect(28, 18, 40, 20), COUNT) );
    Insert( new Button(" Quit", 
        Rect(50, 18, 57, 20), QUIT) );
}

void main()
{
    app.Execute();
}

 void App::HandleEvent( Event &event)
{
    char t[100];
    ostrstream ostr ( t, 100 );

    Window::HandleEvent( event );

    if( event.type == COMMAND )
    {
        switch( event.message )
        {
            case COUNT:
                count++;
                ostr.width(4);
                ostr << count << ends;                  
                number->SetTitle( t );
                break;

            case QUIT:
                Close();
                screen.Clear();
                break;
        }
    }
    event.type = CLEAR;
}
A única diferença entre esse programa e o anterior, é o fato de que mais controles são inseridos na janela pelo construtor de Window, e a função HandleEvent trata dois eventos ao invés de um único: um evento se origina do button COUNT e outro no button QUIT.

Exercício: Um bom exercício a essa altura seria adicionar um novo buttom a esse programa denominado Decrement, e faze-lo decrementar o contador. Para isso, insira um novo button (talvez rearranjando a ordem dos controles dentro do processo) e adicione um novo tratador de eventos. Você poderia ainda adicionar um button denominado Clear para restaurar o contador para zero.

Como um exemplo final, o código seguinte implementa o programa de conversão de temperatura mostrado anteriormente. Esse código demonstra o uso de um controle de entrada.

#include <iostream.h>
#include <strstrea.h>

#include "button.h"
#include "input.h"
#include "label.h"
#include "rect.h"
#include "window.h"

class App: public Window
{
    InputLine *fahr;
    Label *cel;
public:
    App( char *s, Rect &r );

    virtual void HandleEvent( Event &event );
};

const int QUIT = 100;
const int CONVERT = 101;

App app( "Test", Rect(1, 1, 80, 24) );

App::App( char *s, Rect &r ): Window( s, r )
{
    fahr = new InputLine ("fahr", Rect(40, 5, 50, 7) );

    Insert( fahr );

    Insert( new Label("Fahrenheit Temperature:   ", Rect(14, 5, 38, 7)) );
    Insert( new Label("Celsius Temperature:", Rect(14, 9, 38, 11)) );
    cel = new Label("0", Rect(40, 9, 45, 11));
    Insert( cel );

    Insert( new Button(" Convert", Rect(28, 18, 40, 20), CONVERT) );
    Insert( new Button(" Quit", Rect(50, 18, 57, 20), QUIT) );
}

void App::HandleEvent( Event &event)
{
    char s[100];
    char t[100];
    float f;
    istrstream istr( s, 100 );
    ostrstream ostr( t, 100 );

    Window::HandleEvent( event );

    if( event.type == COMMAND )
    {
        switch( event.message )
        {
            case CONVERT:
                fahr->GetText( s, 100 );
                istr >> f;
                ostr.width(6);
                ostr.precision(2);
                ostr << (f-32) * 5/9.0 << ends;
                cel->SetTitle( t );
                break;

            case QUIT:
                Close();
                screen.Clear();
                break;
        }
    }
    event.type = CLEAR;
}

void main()
{
    app.Execute();
}
Quando executar esse programa, use a tecla Tab para mover o apontador entre o controle de entrada e os dois buttons. Pressione a tecla Enter para ativar um button. Quando o controle de entrada estiver apontado, você poderá digitar qualquer caracter e usar a tecla backspace.
Topo
Entendendo a Hierarquia de Classes SST

Para entender os três programas apresentados anteriormente, será útil examinar as diversas classes na hierarquia SST e compreender como elas se relacionam umas com as outras. Vamos começas com a classe base da hierarquia: Rect:
class Rect
{
public:
    int top, bottom, left, right;
    Rect();
    Rect( int x1, int y1, int x2, int y2 );
    int Height();
    void SetSize( Rect &r );
    int Width();
};
A classe Rect é muito simples. Se você olhar o arquivo .CPP ao final desse artigo poderá ver o quanto essa classe é simples realmente - cada função contém apenas uma ou duas linhas de código. A classe contém os quatro dados membro necessários para armazenar as coordenadas do retângulo: cantos superiores esquerdo e direito, cantos inferiores esquerdo e direito. O construtor default atribui valor zero a essas coordenadas. Há um segundo construtor que inicializa as coordenadas com valores específicos. Os membros Height e Width calculam a largura e a altura do retângulo através de subtração simples. O membro SetSize permite que você altere as coordenadas do retângulo a qualquer momento.

A classe Object herda a classe Rect. Você poderia argumentar que a classe Object deveria ser a classe base e ter um relacionamento do tipo usa-um com a classe Rect, ao invés do relacionamento é-um como o utilizado nessa hierarquia. Essa também é uma boa abordagem, portanto altere a hierarquia se você assim preferir.

A classe Object sabe como tratar eventos e deslocar o apontador:

class Object: public Rect
{
protected:
        char *title;
        int takesFocus;
        int focused;
        Screen screen;

public:
    Object();
    Object( char *s, Rect &r );
    virtual ~Object();
    int AcceptsFocus();
    virtual void Draw();
    virtual void HandleEvent( Event &event );
    virtual void ReleaseFocus();
    virtual void SetFocus();
    virtual void SetTitle( char *s );
};
A classe tem dois construtores, o segundo é o mais usado geralmente. Esse construtor recebe um string que funciona como o título para o objeto, e um retângulo que controla o tamanho e a localização do objeto. Um objeto pode aceitar ou não um apontador (um rótulo é um objeto que não admite um apontador, enquanto um button já o admite). Você pode indagar a função AcceptFocus para determinar se o objeto aceita ou não um apontador. Todos os objetos sabem como se desenhar na tela, e você pode desenhá-los invocando a função membro Draw. Essa função desenha um retângulo em torno do objeto. Todos os objetos podem tratar eventos, e fazem isso através da função membro HandleEvent. Você pode assinalar o apontador para um objeto chamando a função membro SetFocus, e retirar o apontador do objeto com ReleaseFocus. Finalmente você pode modificar o string de título do objeto com SetTitle. Examine OBJECT.CPP e verifique que todas as funções membro são extremamente simples.

A classe Object faz uso da classe auxiliar Screen. A classe Screen sabe desenhar na tela e contém três membros: GotoXY que move o cursos na tela. CharXY que escreve um caracter em uma localização específica, e Clear que limpa toda a tela. Examine SCREEN.H para ver o quanto essa classe é simples. A classe usa seqüência de escape ANSI para mover o cursor e para limpar a tela.

A classe Window herda a classe Object. A classe Window é uma Object, e também armazena uma lista de outros objetos. Nessa lista estão todos os objetos contidos na janela. Por conhecer todos os seus controles, a classe Window pode realizar várias coisas:

  • Pode redesenhar-se percorrendo a lista e invocando a função membro Draw de cada controle presente na lista
  • Pode deslocar o apontador entre os objetos, chamando a função ReleaseFocus do objeto apontado no momento, e invocando SetFocus para um outro objeto
  • Pode passar eventos que ela própria não sabe como controlar, para o objeto apontado no momento.

A classe Window contém as seguintes funções:

class Window: public Object
{
protected:
    ObjectList list;
    int running;

public:
    Window();
    Window( char *s, Rect &r );
    ~Window();
    int Close();
    virtual void Draw();
    void Execute();
    virtual void HandleEvent( Event &event );
    void Insert( Object *obj );
    void MoveFocus( char direction );
    void Remove( Object *obj );
    int WindowRunning();
};
Você tem visto, nos programas exemplificados aqui, que a função main deve chamar a função Execute de Window. Essa função, se você examinar o código, é apenas um loop que recebe informações sobre as teclas pressionadas no teclado e as passa para a função HandleEvent. A função HandleEvent move o apontador se a tecla Tab for pressionada, encerra se a tecla ESC for pressionada, ou, em qualquer outro caso, passa o evento para o objeto apontado no momento.

É nas funções Draw e HandleEvent que as funções virtuais entram em cena. Veja a função Draw na classe Window. Essa função percorre toda a lista de objetos, chamando a função Draw. Cada objeto, dependendo de seu tipo (button, entrada, rótulo) tem sua própria implementação da função Draw, de tal modo que cada um pode desenhar-se na tela corretamente. A função HandleEvent na classe Window é um outro bom exemplo. A função Execute chama HandleEvent. Uma vez que herdamos a classe Window, a versão externa de HandleEvent é chamada em primeiro lugar. Em nosso código, nós chamamos novamente a função default Window::HandleEvent para que faça o tratamento automático das teclas TAB e ESC, e também para que dê ao objeto apontado no momento a oportunidade de tratar os eventos que ele conhece. Os controles tanto tratam o evento que conhecem, como limpam o evento para marcá-lo como processado. Quando Window::HandleEvent retorna, nosso código pode processar qualquer outro evento novamente.

Se você examinar EVENT.H, verá que a classe auxiliar event conhece apenas três tipos de eventos:

const char KEYBOARD = 100;
const char COMMAND  = 101;
const char CLEAR = 102;
A Window pode criar um evento KEYSTROKE. Um controle button pode transformar um evento KEYSTROKE em um COMMAND. Clear é usado para marcar um evento como processado.

A função Insert, também vista nos exemplos apresentados aqui, simplesmente insere um controle na lista de controles. Remove é capaz de remover um controle da lista.

Os três controles são muito simples e podem ser facilmente compreendidos quando examinados em seus arquivos .H e .CPP, ao final desse artigo. O controle Label exibe texto. Você pode, a qualquer momento, alterar o texto exibido. Esse controle não admite apontador, portanto nunca trata eventos. O controle Input recebe digitação e a exibe. Armazena a digitação em um string. Você pode inicializar esse string com SetText, e resgatar o string com GetText. A classe Button herda a classe Label. Sua principal finalidade é processar a tecla Enter e transformar esse evento em um evento COMMAND.

Como você pode ver, não há uma montanha de código envolvido nesse processo. O que é interessante notar é o modo como os códigos se interlaçam para formar um todo auto-referenciado. Qualquer grande hierarquia GUI funciona exatamente dessa mesma maneira, mas obviamente contém mais capacidades. O que é importante, no entanto, é que as grandes hierarquias apoiam-se exatamente nos mesmos princípios. Por exemplo, as entradas do usuários são limitadas, geralmente, ao teclado e ao mouse. Isso é um fato. Portanto, mesmo que a diversidade e a variedade de uma hierarquia de classes possa implementar muitos controles e acessórios rebuscados, a função básica é a mesma que se vê aqui na SST.

Topo
Adicionando um Novo Controle

Você poderá aprender bastante mais sobre a SST adicionando um novo controle. Aqui nós vamos criar um novo controle de escala e usá-lo para modificar o programa de conversão de temperatura. O controle de escala se assemelha ao seguinte:
+---------------+
|   20          |
| o--x--------o |
| 0         100 |
+---------------+
Pressionando-se as teclas J e K, a escala será decrementada e incrementada de 1. Pressionando-se as teclas h e i a escala será decrementada e incrementada de uma marca na linha de graduação (notch). Seria muito educativo que você tentasse criar esse controle sozinho, antes de examinar a solução apresentada aqui. Antes de começar, faça a si mesmo as seguintes perguntas:
  • Como fazer para implementar esse controle?
  • Quais os dados membro que serão necessários?
  • Quais as funções membro que serão necessárias?
  • Como enquadrá-lo na hierarquia já existente?

Tente criar uma versão própria desse controle, usando os controles existentes como exemplos.

Aqui está uma solução para o problema:

SCALE.H
class Scale: public Object
{
    int minValue, maxValue, currentValue, numNotches;
    float notchValue;

public:
    Scale();
    Scale( char *s, Rect &r, int min = 0, int max = 100 );
    void Dec( int number = 1 );
    void DecNotch( int number = 1 );
    virtual void Draw();
    int GetPosition();
    virtual void HandleEvent( Event &event );
    void Inc( int number = 1 );
    void IncNotch( int number = 1 );
    void SetPosition( int value );
};
SCALE.CPP
#include "scale.h"

Scale::Scale() : Object(), minValue(0), maxValue(0),
    currentValue(0), numNotches(0), notchValue(0)
{}

Scale::Scale( char *s, Rect &r, int min, int max )
    : Object( s, r ), minValue(min), maxValue(max),
    currentValue(min), numNotches(0), notchValue(0)
{
    numNotches = right - left - 6;
    notchValue = (maxValue - minValue) / (float)numNotches;
}

void Scale::Dec( int number )
{
    if( currentValue - number >= minValue )
    {
        currentValue -= number;
        Draw();
    }
}

void Scale::DecNotch( int number )
{
    if( (currentValue - notchValue * number) >= minValue )
        currentValue -= notchValue * number;
    else
        currentValue = minValue;

    Draw();
}

void Scale::Draw()
{
    int i, pos;

    Object::Draw();

    // draw the ends of the scale
    screen.CharXY( left+2, top+2, 'O' );
    screen.CharXY( right-2, top+2, 'O' );

    // draw the middle of the scale
    for( i = 0; i <= numNotches; i++ )
    {
        screen.CharXY( left+3+i, top+2, '-' );
    }

    // draw the min and max values of the scale
    screen.GotoXY( left+2, top+3 );
    cout << minValue;

    screen.GotoXY( right-2-2, top+3 );
    cout << setiosflags(ios::right) << setw(3) << maxValue;

    // draw the current position
    pos = (left + 3) +  ((currentValue - minValue) / notchValue);
    screen.CharXY( pos, top+2, 'x' );

    // clear old value and draw new value
    for( i = left+1; i < right; i++ )
        screen.CharXY( i, top+1, ' ' );

    screen.GotoXY( pos-2, top+1 );
    cout << setiosflags(ios::right) << setw(3) << currentValue;
}

int Scale::GetPosition()
{
    return currentValue;
}

void Scale::HandleEvent( Event &event )
{
    if( event.type == KEYBOARD )
    {
        switch( event.message )
        {
            case 'j':
                Dec();
                break;

            case 'k':
                Inc();
                break;

            case 'h':
                DecNotch();
                break;

            case 'l':
                IncNotch();
                break;
        }
    }
}

void Scale::Inc( int number )
{
    if( currentValue + number <= maxValue )
    {
        currentValue += number;
        Draw();
    }
}

void Scale::IncNotch( int number )
{
    if( (notchValue * number + currentValue) <= maxValue )
        currentValue += notchValue * number;
    else
        currentValue = maxValue;

    Draw();
}

void Scale::SetPosition( int value )
{
    if( (value >= minValue) && (value <= maxValue) )
        currentValue = value;

    Draw();
}
Topo
F2C2.CPP: Um Programa Exemplo

O seguinte programa recria, usado o controle de escala, o programa e conversão de temperatura visto anteriormente.
#include <iostream.h>
#include <strstrea.h>

#include "button.h"
#include "input.h"
#include "label.h"
#include "rect.h"
#include "scale.h"
#include "window.h"

class App: public Window
{
    Label *cel;
    Scale *scale;
public:
    App( char *s, Rect &r );

    virtual void HandleEvent( Event &event );
};

const int QUIT        = 100;
const int CONVERT    = 101;

App app( "Test", Rect(1, 1, 80, 24) );

App::App( char *s, Rect &r ): Window( s, r )
{
    scale = new Scale("scale", Rect(40,4,66,8), 0, 100 );
    Insert( scale );

    Insert( new Label("Fahrenheit Temperature:   ", Rect(14, 5, 38, 7)) );
    Insert( new Label("Celsius Temperature:", Rect(14, 9, 38, 11)) );
    cel = new Label("0", Rect(40, 9, 45, 11));
    Insert( cel );

    Insert( new Button(" Convert", Rect(28, 18, 40, 20), CONVERT) );
    Insert( new Button(" Quit", Rect(50, 18, 57, 20), QUIT) );
}

void App::HandleEvent( Event &event)
{
    char s[100];

    Window::HandleEvent( event );

    if( event.type == COMMAND )
    {
        switch( event.message )
        {
            case CONVERT:
                int f = scale->GetPosition();

                ostrstream ostr ( s, 100 );
                ostr << (f-32) * 5/9.0 << ends;
                cel->SetTitle( s );
                break;

            case QUIT:
                Close();
                screen.Clear();
                break;
        }
    }
    event.type = CLEAR;
}

void main()
{
    app.Execute();
}
Topo
Conclusão

Nesse artigo você viu como usar uma hierarquia simples de classes GUI, denominada SST. Você também teve a oportunidade de extender a hierarquia e entender seus mecanismos internos. Esperamos que essa experiência o ajude no futuro a entender mais facilmente as grandes hierarquias de classes.
Topo
Listagens do Código

As listagens seguintes contem todo o código das classes SST.
/* ----------------------------------------------------------------------- *
 * b u t t o n . h
 *
 * buttons are windows that display a label and respond to the enter
 * key when they have focus.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __BUTTON_H
#define __BUTTON_H

#include "keys.h"
#include "rect.h"
#include "label.h"

class Button: public Label
{
    int data;
public:
    Button();

    Button( char *s, Rect &r, int d );

    virtual void Draw();

    virtual void HandleEvent( Event &event );

    void SetData( int d );
};

#endif

 /* ----------------------------------------------------------------------- *
 * b u t t o n . c p p
 *
 * buttons are windows that display a label and respond to the enter
 * key when they have focus.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "button.h"

Button::Button(): Label()
{
    takesFocus = 1;
    focused = 0;
}

Button::Button( char *s, Rect &r, int d )
    : data(d), Label( s, r )
{
    takesFocus = 1;
    focused = 0;
}

void Button::Draw()
{
    Label::Draw();
    Object::Draw();
}

void Button::HandleEvent( Event &event )
{
    if( (event.type == KEYBOARD) && (event.message == RETURN) )
    {
        event.type = COMMAND;
        event.message = data;
    }
}

void Button::SetData( int d )
{
    data = d;
}

 /* ----------------------------------------------------------------------- *
 * e v e n t . h
 *
 * class for events
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __EVENT_H
#define __EVENT_H

const char KEYBOARD = 100;
const char COMMAND  = 101;
const char CLEAR    = 102;

class Event
{
public:
    char type;
    char message;

    Event(): type(COMMAND), message(0) {}

    Event( char t, char k ): type(t), message(k) {}
};

#endif

 /* ----------------------------------------------------------------------- *
 * i n p u t . h
 *
 * a simple class to get input from the user.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __INPUT_H
#define __INPUT_H

#include "keys.h"
#include "object.h"
#include "strclass.h"

class InputLine: public Object
{
    String input;

public:
    InputLine();

    InputLine( char *s, Rect &r );

    void GetText( char *s, int n );

    virtual void Draw();

    virtual void HandleEvent( Event &event );

    void SetText( char *s );
};

#endif

 /* ----------------------------------------------------------------------- *
 * i n p u t . c p p
 *
 * a simple class to get input from the user.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "input.h"

InputLine::InputLine()
{
    takesFocus = 1;
    focused = 0;
}

InputLine::InputLine( char *s, Rect &r )
    : Object( s, r )
{
    takesFocus = 1;
    focused = 0;
}

void InputLine::GetText( char *s, int n )
{
    strncpy( s, input.GetString(), n );
}

void InputLine::Draw()
{
    int i;

    Object::Draw();
    if( (Height() > 2) && (Width() > 2) )
    {
        screen.GotoXY( left + 1, top + 1 );
        cout << input.GetString();

        for( i = left + input.GetLength(); i < right - 1; i++ )
            cout << " ";
    }
}

void InputLine::HandleEvent( Event &event )
{
    if( event.type == KEYBOARD )
    {
        switch( event.message )
        {
            case BACKSPACE:
                if( input.GetLength() > 0 )
                {
                    screen.GotoXY( left + input.GetLength(), top + 1 );
                    cout << " ";
                    screen.GotoXY( left + input.GetLength(), top + 1 );
                    input.Remove();
                }
                break;

            case RETURN:
                break;

            default:
                if( event.message == 0 )
                    break;
                if( input.GetLength() + left < right - 1 )
                {
                    input.Insert(event.message);
                    screen.GotoXY( left + input.GetLength(), top + 1 );
                    cout << char(event.message);
                }
                break;
        }
    }
}

void InputLine::SetText( char *s )
{
    input.Clear();
    input.Insert( s );
    Draw();
}

 /* ----------------------------------------------------------------------- *
 * k e y s . h
 *
 * defines useful keyboard key codes.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __KEYS_H
#define __KEYS_H

const char BACKSPACE     = 8;
const char TAB          = 9;
const char RETURN         = 13;
const char SHIFT_TAB    = 15;
const char ESC            = 27;

#endif
 /* ----------------------------------------------------------------------- *
 * l a b e l . h
 *
 * a static label class.  labels do not process events.
 * ----------------------------------------------------------------------- */

#ifndef __LABEL_H
#define __LABEL_H

#include <iostream.h>
#include <iomanip.h>
#include "object.h"

class Label: public Object
{
public:
    Label();

    Label( char *s, Rect &r );

    virtual void Draw();

    void SetTitle( char *s );
};

#endif

 /* ----------------------------------------------------------------------- *
 * l a b e l . c p p
 *
 * a static label class.  labels do not process events.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "label.h"

Label::Label(): Object()
{
    takesFocus = 0;
    focused = 0;
}

Label::Label( char *s, Rect &r )
    : Object( s, r )
{
    takesFocus = 0;
    focused = 0;
}

void Label::Draw()
{
    if( (Height() > 2) && (Width() > 2) )
    {
        screen.GotoXY( left + 1, top + 1 );
        cout << title;
    }
}

void Label::SetTitle( char *s )
{
    Object::SetTitle( s );
    Draw();
}

 /* ----------------------------------------------------------------------- *
 * o b j e c t. h
 *
 * the base class for all screen objects.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __OBJECT_H
#define __OBJECT_H

#include <string.h>
#include "event.h"
#include "rect.h"
#include "screen.h"

class Object: public Rect
{
protected:
    char *title;
    char takesFocus;
    char focused;
    Screen screen;

public:
    Object();

    Object( char *s, Rect &r );

    ~Object();

    char AcceptsFocus();

    virtual void Draw();

    virtual void HandleEvent( Event &event );

    virtual void ReleaseFocus();

    virtual void SetFocus();

    virtual void SetTitle( char *s );
};

#endif

 /* ----------------------------------------------------------------------- *
 * o b j e c t . c p p
 *
 * the base class for all screen objects.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include <iostream.h>
#include "object.h"

Object::Object(): Rect(), title(0), takesFocus(1)
{
}

Object::Object( char *s, Rect &r )
    : Rect( r )
{
    title = new char[ strlen(s) + 1 ];
    strcpy( title, s );
    takesFocus = 1;
}

Object::~Object()
{
    delete [] title;
}

char Object::AcceptsFocus()
{
    return takesFocus;
}

void Object::Draw( )
{
    char style;
    int i;

    if( focused )
        style = '=';
    else
        style = '-';

    for( i = left + 1; i < right; i++ )
    {
        screen.CharXY( i, top, style );
        screen.CharXY( i, bottom, style );
    }
    for( i = top + 1; i < bottom; i++ )
    {
        screen.CharXY( left, i, '|' );
        screen.CharXY( right, i, '|' );
    }
    screen.CharXY( left, top, '+' );
    screen.CharXY( right, top, '+' );
    screen.CharXY( left, bottom, '+' );
    screen.CharXY( right, bottom, '+' );
}

void Object::HandleEvent( Event &event )
{
}

void Object::ReleaseFocus()
{
    focused = 0;
}

void Object::SetFocus()
{
    focused = 1;
}

void Object::SetTitle( char *s )
{
    delete [] title;
    title = new char[ strlen(s) + 1 ];
    strcpy( title, s );
}

 /* ----------------------------------------------------------------------- *
 * o b j l i s t . h
 *
 * a class to manage a list of objects in a window.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __OBJLIST_H
#define __OBJLIST_H

class Object;

typedef struct ObjectNode
{
    Object *object;
    ObjectNode *next;
    ObjectNode *prev;
} ObjectNode;

class ObjectList
{
    ObjectNode *top;
    ObjectNode *bottom;
    ObjectNode *current;

public:
    ObjectList();

    ~ObjectList();

    void Insert( Object *obj );

    Object *GetCurrent();

    Object *GetFirst();

    Object *GetLast();

    Object *GetNext();

    Object *GetPrev();

    void Remove( Object *obj );

    void SetCurrent( Object *obj );
};
#endif

 /* ----------------------------------------------------------------------- *
 * o b j l i s t . c p p
 *
 * implementation of a class to manage a list of objects for a window.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "objlist.h"

ObjectList::ObjectList(): top(0), bottom(0), current(0)
{
}

ObjectList::~ObjectList()
{
    current = top;

    while( current != 0 )
    {
        top = current->next;
        delete current;
        current = top;
    }
}

void ObjectList::Insert( Object *obj )
{
    if( obj == 0 )
        return;

    if( top == 0 )
    {
        top = new ObjectNode;

        top->object = obj;
        top->next = 0;
        top->prev = 0;

        bottom = current = top;
    }
    else
    {
        bottom->next = new ObjectNode;
        bottom->next->prev = bottom;
        bottom = bottom->next;
        bottom->object = obj;
        bottom->next = 0;
    }
}

Object *ObjectList::GetCurrent()
{
    if( current == 0 )
        return 0;
    else
        return current->object;
}

Object *ObjectList::GetFirst()
{
    if( top == 0 )
        return 0;

    current = top;
    return top->object;
}

Object *ObjectList::GetLast()
{
    if( bottom == 0 )
        return 0;

    current = bottom;
    return bottom->object;
}

Object *ObjectList::GetNext()
{
    if( current == 0 )
        return 0;

    current = current->next;

    if( current == 0 )
        return 0;
    else
        return current->object;
}

Object *ObjectList::GetPrev()
{
    if( current == 0 )
        return 0;

    current = current->prev;

    if( current == 0 )
        return 0;
    else
        return current->object;
}

void ObjectList::Remove( Object *obj )
{
    ObjectNode *temp;

    if( obj == 0 )
        return;

    temp = top;

    while( (temp != 0) && (temp->object != obj) )
        temp = temp->next;

    if( temp->object == obj )
    {
        if( temp == top )
        {
            top = temp->next;

            if( top == 0 )
                bottom = 0;
            else
                top->prev = 0;

            if( current == temp )
                current = top;
        }
        else if( temp == bottom )
        {
            bottom = temp->prev;

            if( bottom == 0 )
                top = 0;
            else
                bottom->next = 0;

            if( current == temp )
                current = bottom;
        }
        else
        {
            temp->next->prev = temp->prev;
            temp->prev->next = temp->next;

            if( current == temp )
                current = temp->next;
        }

        delete temp;
    }
}

void ObjectList::SetCurrent( Object *obj )
{
    current = top;

    while( (current != 0) && (current->object != obj) )
        current = current->next;

    if( current == 0 )
        current = top;
}

 /* ----------------------------------------------------------------------- *
 * r e c t . h
 *
 * a simple rectangle class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __RECT_H
#define __RECT_H

class Rect
{
public:
    char top, bottom, left, right;

    Rect();

    Rect( int x1, int y1, int x2, int y2 );

    char Height();

    void SetSize( Rect &r );

    char Width();
};

#endif

 /* ----------------------------------------------------------------------- *
 * r e c t . c p p
 *
 * a simple rectangle class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "rect.h"

Rect::Rect(): top(0), bottom(0),
    right(0), left(0)
{
}

Rect::Rect( int x1, int y1, int x2, int y2 )
    : left(x1), top(y1), right(x2), bottom(y2)
{
}

char Rect::Height()
{
    return bottom - top;
}

void Rect::SetSize( Rect &r )
{
    top = r.top;
    bottom = r.bottom;
    right = r.right;
    left = r.left;
}

char Rect::Width()
{
    return right - left;
}

 /* ----------------------------------------------------------------------- *
 * s c r e e n . h
 *
 * a class to manage an ansi text screen.  allows moving to
 * an x, y location and clearing the screen.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __SCREEN_H
#define __SCREEN_H

#include <iostream.h>


class Screen
{
public:
    Screen( )
    {
    }

    void GotoXY( int x, int y )
    {
        cout << "\033[" << y
            << ";" << x << "H";
    }

    void CharXY( int x, int y, char c)
    {
        GotoXY( x, y );
        cout << c;
    }

    void Clear()
    {
        cout << "\033[2J";
    }
};

#endif

 /* ----------------------------------------------------------------------- *
 * s t r c l a s s . h
 *
 * a simple string class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __STRCLASS_H
#define __STRCLASS_H

#include <string.h>

class String
{
protected:
    char string[80];
    int pos;
public:
    String();

    String( char *s );

    void Clear();

    int GetLength();

    const char *GetString();

    void Insert( char c );

    void Insert( char *s );

    void Remove();
};

#endif

 /* ----------------------------------------------------------------------- *
 * s t r c l a s s . c p p
 *
 * a simple string class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "strclass.h"

String::String(): pos(0)
{
    string[0] = 0;
}

String::String( char *s )
{
    strcpy( string, s );
    pos = strlen( string );
}

void String::Clear()
{
    string[0] = 0;
    pos = 0;
}

int String::GetLength()
{
    return pos;
}

const char *String::GetString()
{
    return string;
}

void String::Insert( char c )
{
    string[pos] = c;
    pos++;
    string[pos] = 0;
}

void String::Insert( char *s )
{
    strcat( string, s );
    pos = strlen( string );
}

void String::Remove()
{
    if( pos > 0 )
    {
        pos--;
        string[pos] = 0;
    }
}

 /* ----------------------------------------------------------------------- *
 * w i n d o w . h
 *
 * the basic window class.  all screen objects are derived from
 * this class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __WINDOW_H
#define __WINDOW_H

#include <conio.h>    // !!!
#include <string.h>
#include <iostream.h>
#include <iomanip.h>
#include "event.h"
#include "keys.h"
#include "object.h"
#include "objlist.h"
#include "rect.h"

const char WIN_NEXT    = 1;
const char WIN_PREV    = 2;

class Window: public Object
{
protected:
    ObjectList list;
    char running;
public:
    Window();

    Window( char *s, Rect &r );

    ~Window();

    int Close();

    virtual void Draw();

    void Execute();

    virtual void HandleEvent( Event &event );

    void Insert( Object *obj );

    void MoveFocus( char direction );

    void Remove( Object *obj );

    char WindowRunning();
};

#endif

 /* ----------------------------------------------------------------------- *
 * w i n d o w . c p p
 *
 * the basic window class.  all screen objects are derived from
 * this class.
 * 
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "window.h"

Window::Window(): running(1)
{
    takesFocus = 1;
}

Window::Window( char *title, Rect &r )
    : Object( title, r )
{
    running = 1;
    takesFocus = 1;
}

Window::~Window()
{
}

int Window::Close( )
{
    running = 0;
    return 1;
}

void Window::Draw( )
{
    Object *temp, *current = list.GetCurrent();

    SetFocus();
    Object::Draw();

    // draw all the child objects
    temp = list.GetFirst();
    while( temp != 0 )
    {
        temp->Draw();
        temp = list.GetNext();
    }

    list.SetCurrent( current );
}

void Window::Execute( )
{
    Event event;

    list.GetLast();
    MoveFocus( WIN_NEXT );
    screen.Clear();
    Draw();

    do
    {
        event.type = KEYBOARD;
        event.message = getch();
        HandleEvent( event );
    } while( WindowRunning() );
}

void Window::HandleEvent( Event &event )
{
    if( event.type == KEYBOARD )
    {
        switch( event.message )
        {
            case TAB:
            case SHIFT_TAB:
                if( list.GetCurrent() == 0 )
                    break;

                if( event.message == TAB )
                    MoveFocus( WIN_NEXT );
                else
                    MoveFocus( WIN_PREV );

                event.type = CLEAR;
                break;

            case ESC:
                Close();
                break;

            default:
                if( list.GetCurrent() == 0 )
                    break;

                list.GetCurrent()->HandleEvent( event );
                break;
        }
    }
}

void Window::Insert( Object *obj )
{
    list.Insert( obj );
    obj->Draw();
}

void Window::MoveFocus( char direction )
{
    int listScanned = 0;
    Object *current = list.GetCurrent();

    if( current == 0 )
        return;

    current->ReleaseFocus();
    current->Draw();

    do
    {
        if( direction == WIN_NEXT )
            current = list.GetNext();
        else
            current = list.GetPrev();

        if( current == 0 )
        {
            listScanned++;
            if( direction == WIN_NEXT )
                current = list.GetFirst();
            else
                current = list.GetLast();
        }
    } while( (listScanned < 2) && !current->AcceptsFocus() );
    if( current->AcceptsFocus() )
    {
        current->SetFocus();
        current->Draw();
    }
    listScanned = 0;
}

void Window::Remove( Object *obj )
{
    Object *current;

    current = list.GetCurrent();
    if( current )
        current->ReleaseFocus();

    list.Remove( obj );

    current = list.GetCurrent();
    if( current )
        current->SetFocus();

    screen.Clear();
    Draw();
}

char Window::WindowRunning()
{
    return running;
}
Se você estiver trabalhando em Windows NT, os seguintes arquivos lhe serão úteis.
/* ----------------------------------------------------------------------- *
 * s c r e e n . h
 *
 * a class to manage a Windows NT console.  allows moving to
 * an x, y location and clearing the screen.
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __SCREEN_H
#define __SCREEN_H

#include <iostream.h>

class Screen
{
public:
    Screen();
    ~Screen();
    void GotoXY( int x, int y );
    void CharXY( int x, int y, char c);
    void Clear();
};

#endif

/* ----------------------------------------------------------------------- *
 * s c r e e n . c p p
 *
 * a class to manage an Windows NT console.  allows moving to
 * an x, y location and clearing the screen.
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

// for NT Screen Console functions
#include <windows.h>
#include "screen.h"

static HANDLE hConsole = 0;
static int instanceCount = 0;

Screen::Screen()
{
    if( instanceCount == 0 )
    {
        hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
    }

    instanceCount++;

    Clear();
}


Screen::~Screen()
{
    instanceCount--;

    if( instanceCount == 0 )
    {
//		  CloseHandle( hConsole );
    }
}


void Screen::GotoXY( int x, int y )
{
    COORD coord;

    coord.X = x - 1;
    coord.Y = y - 1;
	
    SetConsoleCursorPosition( hConsole, coord );
}


void Screen::CharXY( int x, int y, char c)
{
    COORD coord;
    DWORD numWritten;

    coord.X = x - 1;
    coord.Y = y - 1;
	
    SetConsoleCursorPosition( hConsole, coord );
    WriteConsoleOutputCharacter( hConsole, &c, 1, coord, &numWritten );
}


void Screen::Clear()
{
  COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
  BOOL bSuccess;
  DWORD cCharsWritten;
  CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
  DWORD dwConSize; /* number of character cells in the current buffer */

  /* get the number of character cells in the current buffer */
  bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);

  dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
  /* fill the entire screen with blanks */
  bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR) ' ',
      dwConSize, coordScreen, &cCharsWritten);

  /* get the current text attribute */
  bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);

  /* now set the buffer's attributes accordingly */
  bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes,
      dwConSize, coordScreen, &cCharsWritten);

  /* put the cursor at (0, 0) */
  bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);

  return;
}
Se você estiver trabalhando em UNIX, os seguintes arquivos lhe serão úteis.
/* ----------------------------------------------------------------------- *
 * v e r s i o n . h
 *
 * defines whether we are building an msdos or unix version
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

//#define SST_UNIX  // uncomment this line to build a UNIX version

/* ----------------------------------------------------------------------- *
 * k e y s . h
 *
 * defines useful keyboard key codes.
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __KEYS_H
#define __KEYS_H

#include "version.h"

const char BACKSPACE = 8;
const char TAB = 9;

#ifdef SST_UNIX
        const char RETURN = 10;
#else
        const char RETURN = 13;
#endif

const char SHIFT_TAB = 15;
const char ESC = 27;

#endif

/* ----------------------------------------------------------------------- *
 * w i n d o w . h
 *
 * the basic window class.  all screen objects are derived from
 * this class.
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#ifndef __WINDOW_H
#define __WINDOW_H

#include "version.h"

#ifdef SST_UNIX
    #include <stdio.h>
    #include <ctype.h>
    #include <termio.h>
#else
    #include <conio.h> // dos specific
#endif

#include <string.h>
#include <iostream.h>
#include <iomanip.h>
#include "event.h"
#include "keys.h"
#include "object.h"
#include "objlist.h"
#include "rect.h"

const char WIN_NEXT	= 1;
const char WIN_PREV	= 2;

class Window: public Object
{
protected:
    ObjectList list;
    int running;

public:
    Window();

    Window( char *s, Rect &r );

    ~Window();

    int Close();

    virtual void Draw();

    void Execute();

    virtual void HandleEvent( Event &event );

    void Insert( Object *obj );

    void MoveFocus( char direction );

    void Remove( Object *obj );

    int WindowRunning();
};

#endif

/* ----------------------------------------------------------------------- *
 * w i n d o w . c p p
 *
 * the basic window class.  all screen objects are derived from
 * this class.
 *
 * Copyright 1996 by Interface Technologies, Inc. All Rights Reserved.
 * ----------------------------------------------------------------------- */

#include "window.h"

#ifdef SST_UNIX
    static struct termio ostate;
#endif

Window::Window(): running( 0 )
{
    #ifdef SST_UNIX
        struct  termio  nstate;

        ioctl(0, TCGETA, &ostate);
        nstate = ostate;
        nstate.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
        nstate.c_cc[VMIN] = 1;
        nstate.c_cc[VTIME] = 0;
        ioctl(0, TCSETAW, &nstate);
    #endif

    takesFocus = 1;
}

Window::Window( char *title, Rect &r )
    : running( 0 ), Object( title, r )
{
    #ifdef SST_UNIX
        struct  termio  nstate;

        ioctl(0, TCGETA, &ostate);
        nstate = ostate;
        nstate.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
        nstate.c_cc[VMIN] = 1;
        nstate.c_cc[VTIME] = 0;
        ioctl(0, TCSETAW, &nstate);
    #endif

    takesFocus = 1;
}

Window::~Window()
{
    #ifdef SST_UNIX
        ioctl( 0, TCSETAW, &ostate );
    #endif
}

int Window::Close( )
{
    running = 0;
    return 1;
}

void Window::Draw( )
{
    Object *temp, *current = list.GetCurrent();

    SetFocus();
    Object::Draw();

    // draw all the child objects
    temp = list.GetFirst();
    while( temp != 0 )
    {
        temp->Draw();
        temp = list.GetNext();
    }

    list.SetCurrent( current );
}

void Window::Execute( )
{
    Event event;

    // make cin and cout work with buffers of length 1
    cin.setf( ios::unitbuf );
    cout.setf( ios::unitbuf  );
    
    // we are now up and running
    running = 1;

    list.GetLast();
    MoveFocus( WIN_NEXT );
    screen.Clear();
    Draw();

    do
    {
        event.type = KEYBOARD;

        #ifdef SST_UNIX
            event.message = getchar();
        #else
            event.message = getch();
        #endif

        HandleEvent( event );
    } while( WindowRunning() );
}

void Window::HandleEvent( Event &event )
{
    if( event.type == KEYBOARD )
    {
        switch( event.message )
        {
            case TAB:
            case SHIFT_TAB:
                if( list.GetCurrent() == 0 )
                    break;

                if( event.message == TAB )
                    MoveFocus( WIN_NEXT );
                else
                    MoveFocus( WIN_PREV );

                event.type = CLEAR;
                break;

            case ESC:
                Close();
                break;

            default:
                if( list.GetCurrent() == 0 )
                    break;

                list.GetCurrent()->HandleEvent( event );
                break;
        }
    }
}

void Window::Insert( Object *obj )
{
    list.Insert( obj );
    if( WindowRunning() )
        obj->Draw();
}

void Window::MoveFocus( char direction )
{
    int listScanned = 0;
    Object *current = list.GetCurrent();

    if( current == 0 )
        return;

    current->ReleaseFocus();
    current->Draw();

    do
    {
        if( direction == WIN_NEXT )
            current = list.GetNext();
        else
            current = list.GetPrev();

        if( current == 0 )
        {
            listScanned++;
            if( direction == WIN_NEXT )
                current = list.GetFirst();
            else
                current = list.GetLast();
        }
    } while( (listScanned < 2) && !current->AcceptsFocus() );

    if( current->AcceptsFocus() )
    {
        current->SetFocus();
        current->Draw();
    }
    listScanned = 0;
}

void Window::Remove( Object *obj )
{
    Object *current;

    current = list.GetCurrent();
    if( current )
        current->ReleaseFocus();

    list.Remove( obj );

    current = list.GetCurrent();
    if( current )
        current->SetFocus();

    screen.Clear();
    Draw();
}

int Window::WindowRunning()
{
    return running;
}
Topo
© 1998 Interface Technologies, Inc by Marshall Brain & Kelly Campbell
Tradução de Dagoberto Haele Arnaut

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |