{*******************************************************************************
 *                         Stifte und Mäuse Version 2.3.2                      *
 *                (c) 2001 Ulrich Borghoff    Gymnasium Wilnsdorf              *
 *                           eMail: bgh_gywi@yahoo.de                          *
 * *****************************************************************************
 * Diese SuM Version dürfte auf allen Delphi Versionen ab 1.16 und unter allen *
 * Kylix Versionen laufen.                                                     *
 *******************************************************************************
 * Die Anpassungen an Kylix (Delphi für Linux) wurden freundlicherweise von    *
 *                  Anton und Friedrich Hattendorf vorgenommen.                *
 *      eMail: friedrich@hattendoerfer.de      Web : www.hattendoerfer.de      *
 *******************************************************************************
 * Bitte ändern Sie diese Datei nicht ab, sondern teilen den Entwicklern Ihre  *
 * Verbesserungsvorschläge oder Fehler (und ggf. deren Berichtigung) mit.      *
 *                                                                             *
 * Für den Unterricht sollten Sie nur die mSuM.dcu Datei verwenden. Diese muss *
 * sich im Ordner ..delphi/lib befinden. Die Angabe "uses mSuM" ist dann schon *
 * ausreichend.                                                                *
 *                                                                             *
 * Danke für Ihre Mitarbeit.                                                   *
 *******************************************************************************}

unit mSum;

interface

uses
   mListe,
  {$IFDEF WIN32}
    winprocs, wintypes, messages, dialogs, forms, graphics, controls,
  {$ENDIF}
  {$IFDEF LINUX}
    QForms, QGraphics, QControls, QDialogs,
  {$ENDIF} SysUtils, classes ;

const VERSION ='Stifte und Mäuse Version 2.3.2';
      WAHR=true;
      FALSCH=false;
type
  GanzeZahl=longint;
  Zahl=real;
  Wahrheitswert=boolean;
  Zeichen=char;
  Zeichenkette=string;

{Bildschirm *******************************************************************}
type Bildschirm = class (TObject)

  public
    constructor init;
    procedure loescheAlles;
    function breite:GanzeZahl;
    function hoehe:GanzeZahl;
    destructor gibFrei;
  protected { Protected,da diese Methode nur in dieser Unit verfügbar sein soll}
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
end;

{Tastatur *********************************************************************}

type Tastatur=class(tObject)

  public
  constructor init;
  function wurdeGedrueckt:Wahrheitswert;
  function zeichen:Zeichen;
  procedure weiter;
  destructor gibFrei;

  private
   zTastaturfehler : byte;
end;

{Maus *************************************************************************}
type Maus=class(tObject)
  public
    constructor init;
    function istGedrueckt:Wahrheitswert;
    function doppelklick:Wahrheitswert;
    function hPosition:Zahl;
    function vPosition:Zahl;
    destructor gibFrei;
  private
    function linksGedrueckt:Wahrheitswert;
    function linksGeKlickt:Wahrheitswert;
    function linksDoppelGeklickt:Wahrheitswert;
    function rechtsGedrueckt:Wahrheitswert;
    function rechtsGeklickt:Wahrheitswert;
    function rechtsDoppelGeklickt:Wahrheitswert;
  private
    zHPos,zVPos : GanzeZahl;
    zLinksGedrueckt : Wahrheitswert;
    zLinksGeklickt:Wahrheitswert;
    zLinksDoppelGeklickt:Wahrheitswert;
    zRechtsGedrueckt : Wahrheitswert;
    zRechtsGeklickt:Wahrheitswert;
    zRechtsDoppelGeklickt:Wahrheitswert;
    procedure MouseEvent;
end;

{Stift ************************************************************************}
 type
  TZustand = (zsOben,zsUnten);
  TModus   = (moNormal,moRadiere,moWechsle);

  Stift=class(tObject)
  protected
    kenntCanvas  : TCanvas;
    hatStift     : TPen;
    hatPinsel    : TBrush;
    zFarbe       : TColor;
    zZustand     : TZustand;
    zFuellMuster : TBrushStyle;
    zLoeschFarbe : TColor;
    zDicke       : GanzeZahl;
    zWinkel      : Zahl;
    zModus       : TModus;
    zx,zy        : Zahl;
    procedure schreibe(ptext: Zeichenkette); virtual;
  public
    constructor init; virtual;
    procedure bewegeUm(pd:Zahl); virtual;
    procedure bewegeBis(px,py:Zahl); virtual;
    procedure dreheUm(pWinkel: Zahl); virtual;
    procedure dreheBis(pwinkel:Zahl); virtual;
    procedure runter; virtual;
    procedure hoch; virtual;
    procedure normal; virtual;
    procedure radiere; virtual;
    procedure wechsle; virtual;
    procedure zeichneRechteck(pBreite,pHoehe:Zahl); virtual;
    procedure zeichneKreis(pRadius:Zahl); virtual;
    function hPosition: Zahl; virtual;
    function vPosition: Zahl; virtual;
    function winkel: Zahl; virtual;
    procedure schreibeText(pText: Zeichenkette); virtual;
    procedure schreibeZahl(pZahl: Zahl); virtual;
    destructor gibFrei;
  end;

{Buntstift ********************************************************************}
  const
    ROT=clRed;
    BRAUN=clMaroon;
    HELLMAGENTA=clFuchsia;
    PINK=clFuchsia;
    MAGENTA=clFuchsia;
    SCHWARZ=clBlack;
    DUNKELGRAU=clDkGray;
    GRAU=clGray;
    HELLGRAU=clLtGray;
    BLAU=clBlue;
    DUNKELBLAU=clNavy;
    HELLBLAU=clAqua;
    CYAN=clAqua;
    GRUEN=clGreen;
    HELLGRUEN=clLime;
    DUNKELGRUEN=clOlive;
    GELB=clYellow;
    WEISS=clWhite;
    ORANGE=310000;
    GELOESCHT=0;
    DURCHSICHTIG=0;
    GEFUELLT=1;
    GEKREUZT=2;
    SCHRAFFIERT=3;
    LINIERT=4;

    STANDARDSTIL=0;
    FETT=1;
    KURSIV = 2;
    UNTERSTRICHEN = 4;
    DURCHGESTRICHEN = 8;
type
  Buntstift = class(Stift)
  protected
    kenntForm       : TForm;
    zFuellFarbe : TColor;
    zStiftStil  : TPenStyle;
    zSchriftGroesse : GanzeZahl;

  public
    constructor init; override;
    procedure normal; override;
    procedure radiere; override;
    procedure setzeFarbe(pFarbe: GanzeZahl);
    procedure setzeLinienBreite(pBreite:GanzeZahl);
    procedure setzeFuellMuster(pMuster: GanzeZahl);
    procedure setzeSchriftGroesse(pGroesse:GanzeZahl);
    procedure setzeSchriftStil(pStil:GanzeZahl);
    procedure setzeSchriftArt(pArt:Zeichenkette);
    function textbreite(pText:Zeichenkette):GanzeZahl;
    function zahlbreite(pZahl:Zahl):GanzeZahl;
    procedure setzeDicke(pDicke:GanzeZahl);
  end;

var STANDARDSCHRIFT : Zeichenkette;
   {Für Schüler eine Konstante, wird bei der Initialisierung des Buntstifts
    je nach Systemeinstellung belegt.}

{Anwendung ********************************************************************}
type Anwendung=class
  hatBildschirm:Bildschirm;
  hatMaus:Maus;
  hatTastatur:Tastatur;
  constructor init; virtual;
  procedure fuehreAus; virtual; abstract;
  function gibBildschirm:bildschirm;
  function gibMaus:maus;
  function gibTastatur:tastatur;
  destructor gibFrei; virtual;
end;

{Ereignisanwendung ************************************************************}
type Ereignisanwendung=class(Anwendung)
  zbeendet:Wahrheitswert;
  zMausGedrueckt:Wahrheitswert;
  zAlteHPosition,zAlteVPosition:Zahl;
  constructor init; override;
  procedure fuehreAus; override;
  procedure beenden;
  procedure bearbeiteTaste(pZeichen:Zeichen); virtual;
  procedure bearbeiteMausdruck(ph,pv:Zahl); virtual;
  procedure bearbeiteMausLos(ph,pv:Zahl); virtual;
  procedure bearbeiteDoppelklick(ph,pv:Zahl); virtual;
  procedure bearbeiteMausBewegt(ph,pv:Zahl); virtual;
  procedure bearbeiteLeerlauf; virtual;
end;

{EreignisBearbeiter ************************************************************}
 type
    EreignisBearbeiter = class(TObject)
    public
      constructor init; virtual;
      procedure bearbeiteTaste(pZeichen:Zeichen);virtual;
      procedure bearbeiteMausDruck(pHPosition,pVPosition:Zahl);virtual;
      procedure bearbeiteMausLos(pHPosition,pVPosition:Zahl);virtual;
      procedure bearbeiteMausBewegt(pHPosition,pVPosition:Zahl);virtual;
      procedure bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);virtual;
      procedure bearbeiteLeerlauf;virtual;
      destructor gibFrei; virtual;
    end;

{Ereignisverteiler ************************************************************}
    type
    Ereignisverteiler = class(Liste)
      constructor init; override;
      procedure meldeAn (pNeu : EreignisBearbeiter);
      procedure bearbeiteTaste(pZeichen:Zeichen);
      procedure bearbeiteMausDruck(pHPosition,pVPosition:Zahl);
      procedure bearbeiteMausLos(pHPosition,pVPosition:Zahl);
      procedure bearbeiteMausBewegt(pHPositioninH,pHPositioninV:Zahl);
      procedure bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);
      procedure bearbeiteLeerlauf;
      destructor gibFrei; override;
    end;

{EreignisBearbeiterAnwendung  *************************************************}
  type
    EreignisBearbeiterAnwendung = class(EreignisAnwendung)
    hatEreignisverteiler:Ereignisverteiler;
    constructor init; override;
    procedure neuerEreignisbearbeiter(pNeu:ereignisBearbeiter); virtual;
    procedure bearbeiteTaste(pZeichen:Zeichen); override;
    procedure bearbeiteMausDruck(pHPosition,pVPosition:Zahl); override;
    procedure bearbeiteMausLos(pHPosition,pVPosition:Zahl);override;
    procedure bearbeiteMausBewegt(pHPositioninH,pHPositioninV:Zahl);override;
    procedure bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);override;
    procedure bearbeiteLeerlauf;override;
    destructor gibFrei; override;
    end;


{******************************************************************************}

Procedure warte(pZeit: cardinal);
{Zeit in 1000 stel Sekunden (Millisekunden)}

function betrag(pZahl:Zahl):Zahl;
{Ermittelt den Betrag der eingebenen Zahl}

function SuMfarbumrechnung(pFarbe:GanzeZahl):tcolor;
{Wandelt die SuM Farbzahl in dein Delphi tColor um}

function SuMstilumrechnung(pStil:GanzeZahl):tFontStyles;
{Wandelt den SuM Stil in einen Delphi tFontStil um}

const EINGABE = chr(0);
      F1 = chr(1);
      F2 = chr(2);
      F3 = chr(3);
      F4 = chr(4);
      F5 = chr(5);
      F6 = chr(6);
      F7 = chr(7);
      F8 = chr(8);
      F9 = chr(9);
      F10 = chr(10);
      F11 = chr(11);
      F12 = chr(12);
      DRUCK = chr(13);
      ROLLEN = chr(14);
      PAUSE = chr(15);
      TAB = chr(16);
      EINFUEGEN = chr(17);
      ENTFERNEN = chr(18);
      POS1 = chr(19);
      ENDE = chr(20);
      BILDAUF = chr(21);
      BILDAB = chr(22);
      PFEILRECHTS = chr(23);
      PFEILOBEN = chr(24);
      PFEILAUF = chr(25);
      PFEILLINKS = chr(26);
      PFEILUNTEN = chr(27);
      PFEILAB = chr(28);

{******************************************************************************}
implementation
{******************************************************************************}

{Tastaturpuffer (Hilfsklasse) *************************************************}
type Tastaturpuffer=class
  public
    zPuffer:string;
    zLetztes:Zeichen; {zuletzt eingefügtes Zeichen}
    zLetzteZeit : cardinal;
    constructor create;
    procedure check;
    procedure rein(pZeichen : Zeichen);
                                      {Fügt falls möglich ein neues Zeichen ein}
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
                 { Ereignisbehandllung, welche ein neues Sonderzeichen einfügt }
    procedure FormKeyPress(Sender: TObject; var Key: Char);
                       { Ereignisbehandllung, welche ein neues Zeichen einfügt }
    procedure raus;
    function zeichen : Zeichen; {Gibt falls vorhanden das erste Zeichen aus}
    function leer:boolean;
    destructor destroy; override;
end;

{******************************************************************************}

var BFormular:tForm;
    derBildschirm:Bildschirm;
    derTastaturpuffer:Tastaturpuffer;
    Steuerzeichen : set of byte;
{******************************************************************************}

{#k
 die Klasse TMouseStat wurde neu eingeführt,
 da für die Delphi/Kylix Kompatibilität
 keine Systemroutinen verwendet werden sollen
}
type
  TMouseStat = class
  public
    PosX, PosY: integer;
    Down, Clk, Dbl: array[TMouseButton] of Boolean;
    constructor Create;
    procedure MouseDown(Sender: TObject; Button: TMouseButton;
                                            Shift: TShiftState; X, Y: Integer);
    procedure MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
                                                                 X, Y: Integer);
    procedure MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure MouseDblClk(Sender: TObject);
  end;

var
  MouseStat: TMouseStat;

constructor TMouseStat.Create;
begin
  PosX:=0;
  PosY:=0;
end;

procedure TMouseStat.MouseDown(Sender: TObject; Button: TMouseButton; Shift:
                                                    TShiftState; X, Y: Integer);
begin
  PosX:=X;
  PosY:=Y;
  Down[Button]:=True;
end;

procedure TMouseStat.MouseMove(Sender: TObject; Shift: TShiftState;
                                                                X, Y: Integer);
begin
  PosX:=X;
  PosY:=Y;
end;

procedure TMouseStat.MouseUp(Sender: TObject; Button: TMouseButton; Shift:
                                                    TShiftState; X, Y: Integer);
begin
  PosX:=X;
  PosY:=Y;
  Down[Button]:=False;
  Clk[Button]:=True;
end;

procedure TMouseStat.MouseDblClk(Sender: TObject);
begin
  Dbl[mbLeft]:=True;
end;

{Bildschirm *******************************************************************}

constructor Bildschirm.init;
begin
  MouseStat:=TMouseStat.Create;
  Application.Initialize;
  Application.CreateForm(tForm,BFormular);
  BFormular.Caption:=Version;
  BFormular.Top:=5;
  BFormular.Left:=5;
  BFormular.Height:=screen.height-74;
  BFormular.Width:=screen.width-20;
  BFormular.WindowState:=wsMaximized;
  BFormular.Align:=alClient;
  BFormular.Color:=weiss;
  BFormular.OnMouseDown:=MouseStat.MouseDown;
  BFormular.OnMouseMove:=MouseStat.MouseMove;
  BFormular.OnMouseUp:=MouseStat.MouseUp;
  BFormular.OnDblClick:=MouseStat.MouseDblClk;
  BFormular.OnKeyDown:=derTastaturpuffer.FormKeyDown;
  BFormular.OnKeyPress:=derTastaturpuffer.FormKeyPress;
  BFormular.OnCloseQuery:=derBildschirm.FormCloseQuery;
  BFormular.show;
  BFormular.repaint;  {*}
  BFormular.update;   {*}
  derBildschirm:=self;
end;

procedure Bildschirm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose:=True;
  Halt;
end;

procedure Bildschirm.loescheAlles;
begin
  bFormular.Repaint;
end;

function Bildschirm.breite:GanzeZahl;
begin
  Breite:=bFormular.ClientWidth;
end;

function Bildschirm.hoehe:GanzeZahl;
begin
  Hoehe:=bFormular.ClientHeight;
end;

destructor Bildschirm.gibFrei;
begin
  MessageDlg('Programm beendet.',mtCustom,[mbOK],0);
end;

{Tastatur *********************************************************************}

constructor Tastatur.init;
begin
  zTastaturfehler:=0;
end;

function Tastatur.wurdeGedrueckt:Wahrheitswert;
begin
  derTastaturpuffer.check;
  WurdeGedrueckt:=not(derTastaturpuffer.leer);
end;


procedure Tastatur.weiter;
begin
  derTastaturpuffer.check;
  if not(derTastaturpuffer.leer) then derTastaturpuffer.raus
  else
    begin
      case zTastaturfehler of
      0 : begin
            if derBildschirm<> nil then
              MessageDlg('Schwerer Programmierfehler im Umgang mit der Tastatur!',mtError,[mbOK],0);
            inc(zTastaturfehler);
          end;
      1 : begin
            if derBildschirm<> nil then
              MessageDlg ('Weiterer Tastaturfehler! '+
                'Solche Programmierfehler werden nun nur noch durch Tonsignale gemeldet.',mtError,[mbOK],0);
            inc(zTastaturfehler);
          end;
      else Beep;
      end;
    end;
end;

function Tastatur.zeichen:Zeichen;
begin
  derTastaturpuffer.check;
  if not(derTastaturpuffer.leer) then
    Zeichen:=derTastaturpuffer.zeichen
  else
    begin
      case zTastaturfehler of
      0 : begin
            if derBildschirm<> nil then
              MessageDlg('Schwerer Programmierfehler im Umgang mit der Tastatur!',mtError,[mbOK],0);
            inc(zTastaturfehler);
          end;
      1 : begin
            if derBildschirm<> nil then
              MessageDlg('Weiterer Tastaturfehler! '+
              'Solche Programmierfehler werden nun nur noch durch Tonsignale gemeldet.',mtError,[mbOK],0);
            inc(zTastaturfehler);
          end;
      else Beep;
      end;
      Zeichen:=' ';
    end;
end;

destructor Tastatur.gibFrei;
begin
end;

{Maus *************************************************************************}

{#k MouseEvent für Kylix Kompatibilität komplett überarbeitet}

procedure Maus.MouseEvent;
begin
  Application.ProcessMessages;
  zHPos:=MouseStat.PosX;
  zVPos:=MouseStat.PosY;
  zLinksGedrueckt:=MouseStat.Down[mbLeft];
  zLinksGeklickt:=MouseStat.Clk[mbLeft];
  zLinksDoppelGeklickt:=MouseStat.Dbl[mbLeft];
  zRechtsGedrueckt:=MouseStat.Down[mbRight];
  zRechtsGeklickt:=MouseStat.Clk[mbRight];
  zRechtsDoppelGeklickt:=false;
end; {MouseEvent}

constructor Maus.init;
begin
  zLinksGedrueckt:=false;
  zLinksGeklickt:=false;
  zLinksDoppelGeklickt:=false;
  zRechtsGedrueckt:=false;
  zRechtsGeklickt:=false;
  zRechtsDoppelGeklickt:=false;
end;

function Maus.istGedrueckt:Wahrheitswert;
begin
  MouseEvent;
  result:=LinksGedrueckt;
end;

function Maus.doppelKlick:Wahrheitswert;
begin
  MouseEvent;
  result:=LinksDoppelGeklickt;
end;

function Maus.linksGedrueckt:Wahrheitswert;
begin
  MouseEvent;
  LinksGedrueckt:=zLinksGedrueckt;
end;

function Maus.linksGeKlickt:Wahrheitswert;
begin
  MouseEvent;
  LinksGeklickt:=zLinksGeklickt;
  zLinksGeklickt:=false;
end;

function Maus.linksDoppelGeklickt:Wahrheitswert;
begin
  MouseEvent;
  LinksDoppelGeklickt:=zLinksDoppelGeklickt;
  zLinksDoppelGeklickt:=false;
// ub&jd: dbl muss hier zurückgesetzt werden!
  MouseStat.Dbl[mbLeft]:=false
end;

function Maus.rechtsGedrueckt:Wahrheitswert;
begin
  MouseEvent;
  RechtsGedrueckt:=zRechtsGedrueckt;
end;

function Maus.rechtsGeklickt:Wahrheitswert;
begin
  MouseEvent;
  RechtsGeklickt:=zRechtsGeklickt;
  zRechtsGeklickt:=false;
end;

function Maus.rechtsDoppelGeklickt:Wahrheitswert;
begin
  MouseEvent;
  RechtsDoppelGeklickt:=zRechtsDoppelGeklickt;
  zRechtsDoppelGeklickt:=false;
end;

function Maus.hPosition:Zahl;
begin
  MouseEvent;
  HPosition:=zHPos;
end;

function Maus.vPosition:Zahl;
begin
  MouseEvent;
  VPosition:=zVPos;
end;

destructor Maus.gibFrei;
begin
end;

{Stift ************************************************************************}

constructor Stift.init;
begin
  if screen.activeForm=nil then
  begin
    MessageDlg('Ein Stift wurde OHNE oder VOR dem Bildschirm initialisiert.',mtError,[mbOK],0);
    Halt;
  end;

  kenntCanvas:=screen.activeForm.Canvas;
  hatStift:=TPen.Create;
  hatPinsel:=TBrush.Create;
  zFarbe:=clBlack;
  zLoeschFarbe:=screen.activeForm.Color;
  zZustand:=zsOben;
  zWinkel:=0;
  zModus:=moNormal;
  zDicke:=1;
  zFuellmuster:=bsclear;
  zx:=0;
  zy:=0;
  kenntCanvas.moveTo(round(zx),round(zy));

  with hatStift do
  begin
    Color:=screen.activeForm.Color;
    Width:=1;
  end;

  with hatPinsel do
  begin
    color:=zLoeschFarbe;
    Style:=zFuellmuster;
  end;

  self.normal;
end;

procedure Stift.bewegeUm(pd:Zahl);
  var Lx,Ly : Zahl;
begin
  derTastaturpuffer.check;
  lx:=(zx+pd*sin((90-zWinkel)*pi/180));
  ly:=(zy-pd*cos((90-zWinkel)*pi/180));
  bewegeBis(Lx,Ly);
end;

procedure Stift.bewegeBis(px,py:Zahl);
begin
  derTastaturpuffer.check;
  kenntCanvas.MoveTo(round(zx),round(zy));
  zx:=px; zy:=py;
  kenntCanvas.pen:=hatStift;
  kenntCanvas.Brush:=hatPinsel;
  if zZustand=zsOben then kenntCanvas.moveTo(round(px),round(py))
  else
  begin
    kenntCanvas.LineTo(round(px),round(py));
    {$IFDEF WIN32}
                   {#k TCanvas.Pixels ist nicht vorhanden, wird später behoben }
    kenntCanvas.pixels[round(px),round(py)]:=hatStift.color;
                   {#k Notwendig, da lineto den angegebenen Punkt nicht
                    mitzeichnet; Für kylix haben wir noch keine Lösung gefunden}
    {$ENDIF}
  end;
end;

procedure Stift.dreheUm(pWinkel: Zahl);
begin
  derTastaturpuffer.check;
  zWinkel:=zWinkel+pWinkel;
  repeat
    if zWinkel>=360 then zWinkel:=zWinkel-360;
  until zWinkel<360;
end;

procedure Stift.dreheBis(pWinkel:Zahl);
begin
  derTastaturpuffer.check;
  zWinkel:=pWinkel;
end;

procedure Stift.runter;
begin
  derTastaturpuffer.check;
  if zModus=moRadiere then hatStift.Color:=zLoeschFarbe
  else hatStift.Color:=zFarbe;
  zZustand:=zsUnten;
end;

procedure Stift.hoch;
begin
  derTastaturpuffer.check;
  hatStift.Color:= zLoeschFarbe;
  zZustand:=zsOben;
end;

procedure Stift.schreibe(pText: Zeichenkette);
 var AlterZustand:tZustand;
     i:integer;
begin
  {Steuerzeichen sollen nicht ausgegeben werden}
  for i:=1 to length(pText) do
    if ord(pText[i]) in Steuerzeichen then pText[i]:=' ';

  derTastaturpuffer.check;
  AlterZustand:=zZustand;
  hatPinsel.color:=zLoeschfarbe;
  self.Hoch;
  with kenntCanvas do
  begin
    self.BewegeBis(zX,zY);
    kenntCanvas.pen:=hatStift;
    if zModus=moRadiere then kenntCanvas.font.color:=zLoeschFarbe
    else kenntCanvas.font.color:=zFarbe;
    kenntCanvas.brush.style:=zFuellmuster;
    kenntCanvas.brush.Color:=kenntCanvas.pixels[penPos.x,penPos.y];      //Test !!!!!
    TextOut(penPos.x,penPos.y,pText);
    self.BewegeBis(zX+kenntCanvas.TextWidth(pText),zY);
  end;
  if AlterZustand=zsunten then self.runter else self.hoch;
  hatPinsel.color:=zFarbe;
end;

procedure Stift.normal;
begin
  derTastaturpuffer.check;
  zModus:=moNormal;
  hatStift.Width:=zDicke;
  hatStift.Color:=zFarbe;
  hatStift.Mode:=pmcopy	;
end;

procedure Stift.radiere;
begin
  derTastaturpuffer.check;
  zModus:=moRadiere;
  hatStift.Width:=zDicke;
  hatStift.Mode:=pmcopy;
  hatStift.Color:=zLoeschFarbe;
end;

procedure Stift.wechsle;
begin
  derTastaturpuffer.check;
  zModus:=moWechsle;
  hatStift.Width:=zDicke;
  hatStift.Color:=zFarbe;
  hatStift.Mode:=pmNotXor;
end;

procedure Stift.zeichneRechteck(pBreite,pHoehe:Zahl);
 var alterZustand:tZustand;
begin
  derTastaturpuffer.check;
  alterZustand:=zZustand;
  self.runter;
  with kenntCanvas do
  begin
    pen:=hatStift;
    brush:=hatPinsel;
    brush.style:=zFuellmuster;
    rectangle(trunc(zx),trunc(zy),trunc(zx+pBreite),trunc(zy+pHoehe));
  end;
  if alterZustand=zsOben then self.hoch;
end;

procedure Stift.zeichneKreis(pRadius:Zahl);
 var alterZustand:tZustand;
begin
  derTastaturpuffer.check;
  alterZustand:=zZustand;
  self.runter;
  with kenntCanvas do
  begin
    pen:=hatStift;
    brush:=hatPinsel;
    brush.style:=zFuellmuster;
    ellipse(trunc(zx-pRadius),trunc(zy-pRadius),
      trunc(zx+pRadius),trunc(zy+pRadius));
  end;
  if alterZustand=zsOben then self.hoch;
end;

function Stift.hPosition: Zahl;
begin
  derTastaturpuffer.check;
  result:=zx;
end;

function Stift.vPosition: Zahl;
begin
  derTastaturpuffer.check;
  result:=zy;
end;

function Stift.winkel: Zahl;
  begin
    derTastaturpuffer.check;
    result:=zWinkel;
  end;

procedure Stift.schreibeText(pText: Zeichenkette);
begin
  derTastaturpuffer.check;
  self.Schreibe(pText);
end;

procedure Stift.schreibeZahl(pZahl: Zahl);
begin
  derTastaturpuffer.check;
  self.Schreibe(floatToStr(pZahl));
end;

destructor Stift.gibFrei;
begin
  derTastaturpuffer.check;
  hatStift.free;
  hatPinsel.free;
end;

{Buntstift ********************************************************************}

constructor Buntstift.init;
begin
  inherited init;
  kenntForm:=screen.activeForm;
  zFuellFarbe:=zLoeschFarbe;
  zFuellMuster:=bsClear;;
  zStiftStil:=psSolid;
  self.SetzeFarbe(schwarz);
  SetzeFuellmuster(geloescht);

  STANDARDSCHRIFT := kenntCanvas.Font.Name;
end;

procedure Buntstift.normal;
begin
  inherited Normal;
  hatPinsel.Style:=zFuellMuster;
  hatPinsel.Color:=zFuellfarbe;
end;

procedure Buntstift.radiere;
begin
  inherited Radiere;
  hatPinsel.Style:=bsSolid;
  hatPinsel.Color:=zLoeschFarbe;
end;

procedure Buntstift.setzeFarbe(pFarbe: GanzeZahl);
begin
  derTastaturpuffer.check;
  zFarbe:=SuMfarbumrechnung(pFarbe);
  hatStift.Color:=zFarbe;
  zFuellFarbe:=zFarbe;
  hatPinsel.Color:=zFarbe;
  kenntCanvas.font.color:=zFarbe;
end;

procedure Buntstift.setzeLinienBreite(pBreite:GanzeZahl);
begin
  derTastaturpuffer.check;
  self.SetzeDicke(pBreite);
end;

procedure Buntstift.setzeFuellMuster(pMuster: GanzeZahl);
  var lMuster:tBrushStyle;
begin
  derTastaturpuffer.check;
  case pMuster of

       1 : lMuster := bsSolid;
       2 : lMuster := bscross;
       3 : lMuster := bsBDiagonal;
       4 : lMuster := bsHorizontal;
       else lMuster := bsClear;
  end;

  zFuellMuster:=lMuster;
  hatPinsel.Color:=zFuellFarbe;
  hatPinsel.Style:=lMuster;
end;

procedure Buntstift.setzeSchriftGroesse(pGroesse:GanzeZahl);
begin
  derTastaturpuffer.check;
  zSchriftgroesse:=pGroesse;
  kenntCanvas.Font.Height:=pGroesse;
end;

procedure Buntstift.setzeSchriftStil(pStil:GanzeZahl);
begin
  derTastaturpuffer.check;
  kenntCanvas.Font.style:=SuMstilumrechnung(pStil);
end;

procedure Buntstift.setzeSchriftArt(pArt:Zeichenkette);
begin
  derTastaturpuffer.check;
  if (pArt='Standardschrift') or (pArt='StandardSchrift')
      or (pArt='STANDARDSCHRIFT') or(pArt='standardschrift')
  then kenntCanvas.Font.Name:=STANDARDSCHRIFT
  else kenntCanvas.Font.Name:=pArt;
end;

function BuntStift.textbreite(pText:Zeichenkette):GanzeZahl;
begin
  derTastaturpuffer.check;
  textbreite:=kenntCanvas.TextWidth(pText);
end;

function BuntStift.zahlbreite(pZahl:Zahl):GanzeZahl;
begin
  derTastaturpuffer.check;
  Zahlbreite:=kenntCanvas.TextWidth(floattoStr(pZahl));
end;

procedure BuntStift.setzeDicke(pDicke:GanzeZahl);
begin
  derTastaturpuffer.check;
  zDicke:=pDicke;
  hatStift.Width:=zDicke;
end;

{Anwendung ********************************************************************}

constructor Anwendung.init;
begin
  hatBildschirm:=Bildschirm.init;
  hatMaus:=Maus.init;
  hatTastatur:=Tastatur.init;
end;

function Anwendung.gibbildschirm:bildschirm;
begin
  result:=hatBildschirm;
end;

function Anwendung.gibmaus:maus;
begin
  result:=hatMaus;
end;

function Anwendung.gibtastatur:tastatur;
begin
  result:=hatTastatur;
end;

destructor Anwendung.gibFrei;
begin
  hatTastatur.gibFrei;
  hatMaus.gibFrei;
  hatBildschirm.gibFrei;
end;

{Ereignisanwendung ************************************************************}

constructor Ereignisanwendung.init;
begin
  inherited init;
  zbeendet:=false;
  zMausGedrueckt:=false;
  zAlteHPosition:=hatMaus.HPosition;
  zAlteVPosition:=hatMaus.VPosition;
end;

procedure Ereignisanwendung.fuehreAus;
begin
  repeat
    if hatMaus.IstGedrueckt then
    begin
      self.BearbeiteMausDruck(hatMaus.hPosition, hatMaus.vPosition);
      zMausGedrueckt:=true;
    end
    else if zMausGedrueckt then
         begin
           self.BearbeiteMausLos(hatMaus.hPosition, hatMaus.vPosition);
           zMausGedrueckt:=false;
         end;

    if hatMaus.doppelklick then
      self.BearbeiteDoppelklick(hatMaus.hPosition, hatMaus.vPosition);

    if hatTastatur.wurdeGedrueckt then
       begin
	  bearbeiteTaste(hatTastatur.Zeichen);
	  hatTastatur.weiter
       end;

    if (hatMaus.hPosition<>zAlteHPosition) or
    (hatMaus.vPosition<>zAlteVPosition)
    then
    begin
     zAlteHPosition:=hatMaus.HPosition;
     zAlteVPosition:=hatMaus.VPosition;
     self.bearbeiteMausBewegt(zAlteHPosition,zAlteVPosition);
    end;

    self.bearbeiteLeerlauf;
  until zbeendet;
end;

procedure Ereignisanwendung.beenden;
begin
  zbeendet:=true;
end;

procedure Ereignisanwendung.bearbeiteTaste(pZeichen:Zeichen);
begin
{abstrakt}
end;

procedure Ereignisanwendung.bearbeiteMausdruck(ph,pv:Zahl);
begin
{abstrakt}
end;

procedure Ereignisanwendung.bearbeiteMausLos(ph,pv:Zahl);
begin
{abstrakt}
end;

procedure Ereignisanwendung.bearbeiteDoppelklick(ph,pv:Zahl);
begin
{abstrakt}
end;

procedure Ereignisanwendung.bearbeiteMausBewegt(ph,pv:Zahl);
begin
{abstrakt}
end;

procedure Ereignisanwendung.bearbeiteLeerlauf;
begin
{abstrakt}
end;

{Ereignisbearbeiter ************************************************************}

  constructor Ereignisbearbeiter.init;
  begin
  end;

  procedure Ereignisbearbeiter.bearbeiteTaste(pZeichen:Zeichen);
   begin
  end;

  procedure Ereignisbearbeiter.bearbeiteMausDruck(pHPosition,pVPosition:Zahl);
  begin
  end;

  procedure Ereignisbearbeiter.bearbeiteMausLos(pHPosition,pVPosition:Zahl);
  begin
  end;

  procedure Ereignisbearbeiter.bearbeiteMausBewegt(pHPosition,pVPosition:Zahl);
  begin
  end;

  procedure Ereignisbearbeiter.bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);
  begin
  end;

  procedure Ereignisbearbeiter.bearbeiteLeerlauf;
  begin
  end;

  destructor Ereignisbearbeiter.gibFrei;
  begin
  end;

{Ereignisverteiler ************************************************************}

   constructor Ereignisverteiler.init;
   begin
     inherited init;
   end;

   destructor Ereignisverteiler.gibFrei;
   var
     aktuell: EreignisBearbeiter;
   begin
      // zunächst die gespeicherten EreignisBearbeiter freigeben
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.gibFrei;
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.gibFrei;
      end;
      inherited gibFrei // die Listenelemente selbst freigeben
   end;

   procedure Ereignisverteiler.meldeAn (pNeu : EreignisBearbeiter);
   begin
     self.einfuegen(pNeu);
   end;

   procedure Ereignisverteiler.bearbeiteTaste(pZeichen:Zeichen);
   var
     aktuell:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.bearbeiteTaste(pZeichen);
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.bearbeiteTaste(pZeichen);
      end;
   end;

   procedure Ereignisverteiler.bearbeiteMausDruck(pHPosition,pVPosition:Zahl);
   var
     aktuell:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.bearbeiteMausDruck(pHPosition,pVPosition);
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.bearbeiteMausDruck(pHPosition,pVPosition);
      end;
   end;

   procedure Ereignisverteiler.bearbeiteMausLos(pHPosition,pVPosition:Zahl);
   var
     aktuell:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.bearbeiteMausLos(pHPosition,pVPosition);
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.bearbeiteMausLos(pHPosition,pVPosition);
      end;
   end;

   procedure Ereignisverteiler.bearbeiteMausbewegt(pHPositioninH,pHPositioninV:Zahl);
   var
     aktuell:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.bearbeiteMausBewegt(pHPositioninH,pHPositioninV);
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.bearbeiteMausBewegt(pHPositioninH,pHPositioninV);
      end;
   end;

  procedure Ereignisverteiler.bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);
  var
     aktuell:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          aktuell:=EreignisBearbeiter(self.zeigeElement);
          aktuell.bearbeiteDoppelKlick(pHPosition,pVPosition);
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        aktuell:=EreignisBearbeiter(self.zeigeElement);
        aktuell.bearbeiteDoppelKlick(pHPosition,pVPosition);
      end;
   end;

  procedure Ereignisverteiler.bearbeiteLeerlauf;
  var
     kenntEreignisBearbeiter:ereignisBearbeiter;
   begin
      if not self.leer then begin
        self.zumAnfang;
        while not self.letzter do begin
          kenntEreignisBearbeiter:=EreignisBearbeiter(self.zeigeElement);
          kenntEreignisBearbeiter.bearbeiteLeerlauf;
          self.zumNaechsten;
        end;
        //bearbeite letztesListenElement
        kenntEreignisBearbeiter:=EreignisBearbeiter(self.zeigeElement);
        kenntEreignisBearbeiter.bearbeiteLeerlauf;
      end;
   end;

{Ereignisverteiler ************************************************************}

  constructor EreignisBearbeiterAnwendung.init;
  begin
    inherited init;
    hatEreignisverteiler:=Ereignisverteiler.init;
  end;

  procedure EreignisBearbeiterAnwendung.neuerEreignisbearbeiter(pNeu:ereignisBearbeiter);
  begin
    hatEreignisverteiler.einfuegen(pNeu);
  end;

  procedure EreignisBearbeiterAnwendung.bearbeiteTaste(pZeichen:Zeichen);
  begin
    hatEreignisverteiler.bearbeiteTaste(pZeichen);
  end;

  procedure EreignisBearbeiterAnwendung.bearbeiteMausDruck(pHPosition,pVPosition:Zahl);
  begin
    hatEreignisverteiler.bearbeiteMausDruck(pHPosition,pVPosition);
  end;

  procedure EreignisBearbeiterAnwendung.bearbeiteMausLos(pHPosition,pVPosition:Zahl);
  begin
    hatEreignisverteiler.bearbeiteMausLos(pHPosition,pVPosition);
  end;

  procedure EreignisBearbeiterAnwendung.bearbeiteMausBewegt(pHPositioninH,pHPositioninV:Zahl);
  begin
    hatEreignisverteiler.bearbeiteMausBewegt(pHPositioninH,pHPositioninV);
  end;

  procedure EreignisBearbeiterAnwendung.bearbeiteDoppelKlick(pHPosition,pVPosition:Zahl);
  begin
    hatEreignisverteiler.bearbeiteDoppelKlick(pHPosition,pVPosition);
  end;

  procedure EreignisbearbeiterAnwendung.bearbeiteLeerlauf;
  begin
    hatEreignisverteiler.bearbeiteLeerlauf;
  end;

  destructor EreignisbearbeiterAnwendung.gibFrei;
  begin
    hatEreignisverteiler.gibFrei;
    inherited gibFrei;
  end;





{ Tastaturpuffer (Hilfsklasse) ************************************************}

{#k Die Klasse Tastaturpuffer wurde so umgeschrieben, das sie auf Ereignisse der
                                               TForm Komponente reagieren kann }

constructor Tastaturpuffer.create;
begin
  zPuffer:='';
  zLetztes:=chr(0);
end;

procedure Tastaturpuffer.FormKeyDown(Sender: TObject;
                                             var Key: Word; Shift: TShiftState);
                 { Ereignisbehandllung, welche ein neues Sonderzeichen einfügt }
                                { Hier werden nur die Funtionstasten behandelt }
begin
  {$IFDEF LINUX}
       {#k Die Werte für die Sonderzeichen sind in Kylix und Delphi verschieden}
    if Key=4096 then
    begin
      MessageDlg('Der Benutzer beendet das Programm vorzeitig mit der ESC-Taste'
                                                            ,mtCustom,[mbOK],0);
      Halt;
    end;
    case Key of
      4097 : self.rein(TAB);
      4100 : self.rein(EINGABE);
      4144 : self.rein(F1);
      4145 : self.rein(F2);
      4146 : self.rein(F3);
      4147 : self.rein(F4);
      4148 : self.rein(F5);
      4149 : self.rein(F6);
      4150 : self.rein(F7);
      4151 : self.rein(F8);
      4152 : self.rein(F9);
      4153 : self.rein(F10);
      4154 : self.rein(F11);
      4155 : self.rein(F12);
      4105 : self.rein(DRUCK);
      4134 : self.rein(ROLLEN);
      4104 : self.rein(PAUSE);
      4102 : self.rein(EINFUEGEN);
      4103 : self.rein(ENTFERNEN);
      4113 : self.rein(ENDE);
      4112 : self.rein(POS1);
      4118 : self.rein(BILDAUF);
      4119 : self.rein(BILDAB);
      4114 : self.rein(PFEILLINKS);
      4115 : self.rein(PFEILOBEN);
      4116 : self.rein(PFEILRECHTS);
      4117 : self.rein(PFEILUNTEN);
    end;
  {$ENDIF}
  {$IFDEF WIN32}
    if Key=VK_ESCAPE then
    begin
      MessageDlg('Der Benutzer beendet das Programm vorzeitig mit der ESC-Taste'
                                                            ,mtCustom,[mbOK],0);
      Halt;
    end;
    case Key of
      9  : self.rein(TAB);
      13 : self.rein(EINGABE);
      112: self.rein(F1);
      113: self.rein(F2);
      114: self.rein(F3);
      115: self.rein(F4);
      116: self.rein(F5);
      117: self.rein(F6);
      118: self.rein(F7);
      119: self.rein(F8);
      120: self.rein(F9);
      121: self.rein(F10);
      122: self.rein(F11);
      123: self.rein(F12);
      44 : self.rein(DRUCK);
      145: self.rein(ROLLEN);
      19 : self.rein(PAUSE);
      45 : self.rein(EINFUEGEN);
      46 : self.rein(ENTFERNEN);
      35 : self.rein(ENDE);
      36 : self.rein(POS1);
      33 : self.rein(BILDAUF);
      34 : self.rein(BILDAB);
      37 : self.rein(PFEILLINKS);
      38 : self.rein(PFEILOBEN);
      39 : self.rein(PFEILRECHTS);
      40 : self.rein(PFEILUNTEN);
    end;
  {$ENDIF}
end;

procedure Tastaturpuffer.FormKeyPress(Sender: TObject; var Key: Char);
                       { Ereignisbehandllung, welche ein neues Zeichen einfügt }
begin
  self.rein(Key);
end;

procedure Tastaturpuffer.check;
begin
  Application.ProcessMessages;
end;

procedure Tastaturpuffer.rein(pZeichen : Zeichen);
{Fügt falls möglich ein neues Zeichen ein}
begin
  zPuffer:=zPuffer+pZeichen;
end;

function Tastaturpuffer.zeichen : Zeichen;
{Gibt falls vorhanden das erste Zeichen raus}
begin
 result:=zPuffer[1];
end;

procedure Tastaturpuffer.raus;
{Nimmt das erste Zeichen heraus}
begin
 zPuffer:=copy(zPuffer,2,length(zPuffer));
end;


function Tastaturpuffer.leer:boolean;
begin
  leer:=(length(zPuffer)=0);
end;

destructor Tastaturpuffer.destroy;
begin
end;

{Hilfsmethoden ****************************************************************}

procedure warte(pZeit:cardinal);
begin
   sleep(pZeit);
end;

function betrag(pZahl:Zahl):Zahl;
begin
  result:=abs(pZahl);
end;

function SuMfarbumrechnung(pFarbe:GanzeZahl):tcolor;
begin
 {Farbtabelle mit Bernds Java SuM abgleichen,alle anderen Farben gemäß RGB Modell}
  case pFarbe of
    0 : result:=SCHWARZ;
    1 : result:=BLAU;
    2 : result:=CYAN;
    3 : result:=DUNKELGRUEN;
    4 : result:=GRAU;
    5 : result:=GRUEN;
    6 : result:=HELLGRAU;
    7 : result:=MAGENTA;
    8 : result:=ORANGE;
    9 : result:=PINK;
    10 : result:=ROT;
    11 : result:=WEISS;
    12 : result:=GELB

    else result:=pFarbe;
  end;
end;

function SuMstilumrechnung(pStil:GanzeZahl):tFontStyles;
begin
 result:=[];
 if pStil = STANDARDSTIL then result:=[] else
  begin
    if pStil >= DURCHGESTRICHEN  then
    begin
      pStil:=pStil-DURCHGESTRICHEN;
      result:=result + [fsStrikeout];
    end;
    if pStil >= UNTERSTRICHEN  then
    begin
      pStil:=pStil-UNTERSTRICHEN;
      result:=result + [fsUnderline];
    end;
    if pStil >= KURSIV  then
    begin
      pStil:=pStil-KURSIV;
      result:=result + [fsItalic];
    end;
    if pStil >= FETT  then
    begin
      {pStil:=pStil-FETT;}
      result:=result + [fsBold];
    end;
  end;
end;

{Klasseninitialisierung *******************************************************}

initialization
  derTastaturpuffer:=Tastaturpuffer.create;

Steuerzeichen := [0..28];

end.




