| A Hierarquia de Classes SST | |
|
|
| 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:
SST é, portanto, um valioso instrumento de estudo para programadores C++ iniciantes. |
|
| 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:
SST entende a seguinte semântica de teclado:
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: 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. |
|
| 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:
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();
}
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:
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();
};
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 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();
}
/* ----------------------------------------------------------------------- *
* 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;
}
| © 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 | | |