Anda di halaman 1dari 45

Apresentao -------------------------------------------------------------------------------O conceito de hierarquia de classes um dos de mais difcil compreenso pelos programadores, tanto iniciantes quanto experientes

s em C++. Embora seja simples de definir - uma hierarquia de classes um conjunto de classes que se constri tomando umas como base para as outras e usando os recursos de herana e funes virtuais - e simples de justificar - a principal razo para criar hierarquia de classes reusabilidade bastante difcil sair do plano conceitual e atingir proficincia nesse tema, porque as hierarquias de classes tendem a ser cdigos muito extensos, amplamente autoreferenciados. 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 comear a entender as vantagens da herana e do polimorfismo, e voc ainda ter acesso a vrias tcnicas de projeto que lhe sero teis. O problema com as hierarquias existentes que na maioria dos casos so muito complexas. Por exemplo, a hierarquia de classes MFC contm centenas de classes, milhares de funes membro e dezenas de milhares de linhas de cdigo. 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 grfica com o usurio (GUI), que lhe permite criar uma aplicao 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 prprio nome j indica, super simples. Entretanto, dentro dessa simplicidade, contm trs 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 aplicao simples, adicionar-lhe vrios controles novos, e ainda compreender o cdigo fonte que cria o SST em apenas alguns poucos dias. SST, embora pequena, contm todas as idias 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 portvel, tendo sido compilado sob Visual C++, Borland C++, AT&T C-Front, etc. ainda adaptvel a vrios outros ambientes computacionais. SST , portanto, um valioso instrumento de estudo para programadores C++ iniciantes. Introduo a Hierarquia SST Todas as hierarquias GUI permitem que voc crie objetos GUI padres, tais como buttons, rtulos, barras de scroll, etc. SST no diferente: apenas muito mais simples. SST possibilita que voc crie buttons, rtulos e reas de edio. simples o bastante para ser usada, compreendida e extendida em apenas uns poucos dias. Adicionalmente, voc ainda pode examinar o cdigo fonte (todo o cdigo fonte de SST apresentado ao final desse artigo) e aprender com as tcnicas de herana empregadas nessa implementao. SST segue os padres de uso encontrados na maioria das implementaes GUI. SST uma interface baseada em caractres totalmente portvel. Isso, embora no privilegie a beleza da interface, permite que voc a execute em qualquer

plataforma. Um programa tpico criado com o uso da SST ter uma aparncia similar ao seguinte:

Nessa janela h rtulos estticos - 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 aplicao apresentada implementa uma converso simples de graus Celsius para Fahrenheit. Um programa SST pode conter os seguintes objetos GUI: Uma janela principal Rtulos Buttons reas de entrada de dados SST entende a seguinte semntica de teclado: Deslocar o apontador com a tecla Tab ou Shift-Tab Ativar buttons com a tecla Enter Os controles de edio aceitam digitao normal e backspace. A hierarquia SST contm 10 classes, 6 das quais agrupadas hierarquicamente, como mostrado a seguir:

As quatro restantes so 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 sees seguintes voc vai aprender como criar aplicaes simples usando essas classes, e vai aprender tambm como funciona o cdigo fonte dessas classes.

Criando a Aplicao SST Mais Simples -------------------------------------------------------------------------------Todas as aplicaes SST compartilham vrios elementos fundamentais. Seja qual for o grau de simplicidade, ou de complexidade, voc sempre segue as seguintes etapas em toda aplicao SST: - Deriva sua aplicao 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 aplicao mais simples que voc pode criar uma janela com um buttom Quit. Para criar tal aplicao, voc deve comear 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 cdigo declara uma classe denominada App derivada de Window, e essa classe derivada sobrescreve o construtor e a funo HandleEvent. Em seguida o programa cria uma instncia dessa classe como uma varivel global e passa o string Test (que ser o nome da janela) e o tamanho e a posio de Window na tela. Assuma que a tela tem dimenso de 80 caractres de largura por 24 de altura. O tamanho especificado preenche a tela. A seguir o cdigo chama o membro Execute dentro da funo main para iniciar o processo. Voc vai ento 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 funo Insert cria uma nova instncia da classe Button, passando o ttulo do button (Quit), a localizao e o tamanho, e uma identificao (ID) 100 na constante denominada QUIT. Essa identificao ser usada para manejar eventos gerados pelo button. Voc deve ento sobrescrever a funo 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 cdigo primeiramente chama a funo HandleEvent default da classe Window, para que trate automaticamente qualquer evento j conhecido pela funo. Quaisquer eventos no tratados pela funo HandleEvent so ento testados para verificar se so eventos COMMAND. Se forem, so testados para verificar se so originados pelo button Quit. Se forem, a aplicao encerrada com fechamento da janela e limpeza da tela. Se voc montar essas peas e executar a aplicao, vai obter a apresentao de uma janela contendo um button Quit. Quando voc pressionar a tecla Enter (para ativar o button) a aplicao ser encerrada. A maneira mais fcil de compilar essa aplicao tomar todas as classes SST (apresentadas no final desse artigo) coloc-las em um diretrio. Coloque o cdigo 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 prefervel criar uma aplicao text-based console. Execute a aplicao. SST assume que a janela de entrada trata seqncia de caractres ANSI. Assim, se voc estiver usando MS-DOS ou Windows 95, defina ANSI.SYS em seu arquivo config.sys, ou seja, adicione a especificao DEVICE=ANSI.SYS ao contedo do arquivo config.sys e reinicie o computador. Se voc estiver usando Windows NT ou UNIX, devem ser fornecidas as verses especiais dos arquivos apropriados.

A seguir, um programa um pouco mais avanado que faz uso de dois controles disponveis na SST (rtulos 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 diferena entre esse programa e o anterior, o fato de que mais controles so inseridos na janela pelo construtor de Window, e a funo HandleEvent trata dois eventos ao invs de um nico: um evento se origina do button COUNT e outro no button QUIT. Exerccio: Um bom exerccio 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 cdigo seguinte implementa o programa de converso de temperatura mostrado anteriormente. Esse cdigo 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. Entendendo a Hierarquia de Classes SST -------------------------------------------------------------------------------Para entender os trs programas apresentados anteriormente, ser til examinar as diversas classes na hierarquia SST e compreender como elas se relacionam umas com as outras. Vamos comeas 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 funo contm apenas uma ou duas linhas de cdigo. A classe contm os quatro dados membro necessrios para armazenar as coordenadas do retngulo: 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 especficos. Os membros Height e Width calculam a largura e a altura do retngulo atravs de subtrao simples. O membro SetSize permite que voc altere as coordenadas do retngulo 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 invs do relacionamento -um como o utilizado nessa hierarquia. Essa tambm 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 ttulo para o objeto, e um retngulo que controla o tamanho e a localizao do objeto. Um objeto pode aceitar ou no um apontador (um rtulo um objeto que no admite um apontador, enquanto um button j o admite). Voc pode indagar a funo AcceptFocus para determinar se o objeto aceita ou no um apontador. Todos os objetos sabem como se desenhar na tela, e voc pode desenh-los invocando a funo membro Draw. Essa funo desenha um retngulo em torno do objeto. Todos os objetos podem tratar eventos, e fazem isso atravs da funo membro HandleEvent. Voc pode assinalar o apontador para um objeto chamando a funo membro SetFocus, e retirar o apontador do objeto com ReleaseFocus. Finalmente voc pode modificar o string de ttulo do objeto com SetTitle. Examine OBJECT.CPP e verifique que todas as funes membro so extremamente simples. A classe Object faz uso da classe auxiliar Screen. A classe Screen sabe desenhar na tela e contm trs membros: GotoXY que move o cursos na tela. CharXY que escreve um caracter em uma localizao especfica, e Clear que limpa toda a tela. Examine SCREEN.H para ver o quanto essa classe simples. A classe usa seqncia 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 tambm armazena uma lista de outros objetos. Nessa lista esto todos os objetos contidos na janela. Por conhecer todos os seus controles, a classe Window pode realizar vrias coisas: - Pode redesenhar-se percorrendo a lista e invocando a funo membro Draw de cada controle presente na lista - Pode deslocar o apontador entre os objetos, chamando a funo ReleaseFocus do objeto apontado no momento, e invocando SetFocus para um outro objeto - Pode passar eventos que ela prpria no sabe como controlar, para o objeto apontado no momento. A classe Window contm as seguintes funes:

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 funo main deve chamar a funo Execute de Window. Essa funo, se voc examinar o cdigo, apenas um loop que recebe informaes sobre as teclas pressionadas no teclado e as passa para a funo HandleEvent. A funo 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 funes Draw e HandleEvent que as funes virtuais entram em cena. Veja a funo Draw na classe Window. Essa funo percorre toda a lista de objetos, chamando a funo Draw. Cada objeto, dependendo de seu tipo (button, entrada, rtulo) tem sua prpria implementao da funo Draw, de tal modo que cada um pode desenhar-se na tela corretamente. A funo HandleEvent na classe Window um outro bom exemplo. A funo Execute chama HandleEvent. Uma vez que herdamos a classe Window, a verso externa de HandleEvent chamada em primeiro lugar. Em nosso cdigo, ns chamamos novamente a funo default Window::HandleEvent para que faa o tratamento automtico das teclas TAB e ESC, e tambm 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 cdigo pode processar qualquer outro evento novamente. Se voc examinar EVENT.H, ver que a classe auxiliar event conhece apenas trs 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 funo Insert, tambm vista nos exemplos apresentados aqui, simplesmente insere um controle na lista de controles. Remove capaz de remover um controle da lista.

Os trs controles so 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 no admite apontador, portanto nunca trata eventos. O controle Input recebe digitao e a exibe. Armazena a digitao 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, no h uma montanha de cdigo envolvido nesse processo. O que interessante notar o modo como os cdigos se interlaam para formar um todo auto-referenciado. Qualquer grande hierarquia GUI funciona exatamente dessa mesma maneira, mas obviamente contm mais capacidades. O que importante, no entanto, que as grandes hierarquias apoiam-se exatamente nos mesmos princpios. Por exemplo, as entradas do usurios so 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 acessrios rebuscados, a funo bsica a mesma que se v aqui na SST. Adicionando um Novo Controle -------------------------------------------------------------------------------Voc poder aprender bastante mais sobre a SST adicionando um novo controle. Aqui ns vamos criar um novo controle de escala e us-lo para modificar o programa de converso 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 graduao (notch). Seria muito educativo que voc tentasse criar esse controle sozinho, antes de examinar a soluo apresentada aqui. Antes de comear, faa a si mesmo as seguintes perguntas: Como fazer para implementar esse controle? Quais os dados membro que sero necessrios? Quais as funes membro que sero necessrias? Como enquadr-lo na hierarquia j existente? Tente criar uma verso prpria desse controle, usando os controles existentes como exemplos. Aqui est uma soluo 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(); }

F2C2.CPP: Um Programa Exemplo -------------------------------------------------------------------------------O seguinte programa recria, usado o controle de escala, o programa e converso 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(); }

Concluso -------------------------------------------------------------------------------Nesse artigo voc viu como usar uma hierarquia simples de classes GUI, denominada SST. Voc tambm teve a oportunidade de extender a hierarquia e entender seus mecanismos internos. Esperamos que essa experincia o ajude no futuro a entender mais facilmente as grandes hierarquias de classes.

Listagens do Cdigo -------------------------------------------------------------------------------As listagens seguintes contem todo o cdigo das classes SST.

/* ----------------------------------------------------------------------- * *button.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 /* ----------------------------------------------------------------------- * *button.cpp * * 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;

} /* ----------------------------------------------------------------------- * *event.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 /* ----------------------------------------------------------------------- * *input.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 /* ----------------------------------------------------------------------- * *input.cpp * * 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(); } /* ----------------------------------------------------------------------- * *keys.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 /* ----------------------------------------------------------------------- * *label.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 /* ----------------------------------------------------------------------- * *label.cpp * * 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 /* ----------------------------------------------------------------------- * *object.cpp * * 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 ); } /* ----------------------------------------------------------------------- * *objlist.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 /* ----------------------------------------------------------------------- * *objlist.cpp * * 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; } /* ----------------------------------------------------------------------- *

*rect.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 /* ----------------------------------------------------------------------- * *rect.cpp * * 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; } /* ----------------------------------------------------------------------- * *screen.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 /* ----------------------------------------------------------------------- * *strclass.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 /* ----------------------------------------------------------------------- * *strclass.cpp * * 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; } } /* ----------------------------------------------------------------------- * *window.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

/* ----------------------------------------------------------------------- * *window.cpp * * 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 sero teis.

/* ----------------------------------------------------------------------- * *screen.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 /* ----------------------------------------------------------------------- * *screen.cpp * * 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 sero teis.

/* ----------------------------------------------------------------------- * *version.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 /* ----------------------------------------------------------------------- * *keys.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 /* ----------------------------------------------------------------------- * *window.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 /* ----------------------------------------------------------------------- * *window.cpp * * 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; }