unit helpWindow;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

const PRETTY_BLINK_COUNT=4; //Wie oft eine markierte Komponente blinken soll
      PRETTY_BLINK_TIME=250;//Wie lange es dauert, bis eine blinkende Komponente
                            //die Farbew echselt

type
  Thelp = class(TForm)
    helper: TPanel; //Panel das im Tipmodus an SetCapture übergeben wird, so dass
                    //über sein MouseUp-Ereignis herausgefunden wird, auf welche
                    //Komponente geklickt uwrde
    Button1: TButton; //Beendenbutton
    ListBox1: TListBox; //Themenauswahl
    Text0: TScrollBox; //Allgemeine Hilfe
    Text1: TScrollBox; //Hilfe zum 2D-Modus
    Text2: TScrollBox; //Hilfe zum 3D-Modus
    Text3: TScrollBox; //Hilfe zum Farbdialog
    Text4: TScrollBox; //Hilfe zur Statistik
    //Labels die mit über Linklabel erreicht werden können
    anchorLabel0: TLabel;    anchorLabel1: TLabel;    anchorLabel100: TLabel;
    anchorLabel101: TLabel;    anchorLabel102: TLabel;    anchorLabel103: TLabel;
    anchorLabel2: TLabel;    anchorLabel200: TLabel;    anchorLabel201: TLabel;
    anchorLabel202: TLabel;    anchorLabel203: TLabel;    anchorLabel204: TLabel;
    anchorLabel300: TLabel;    anchorLabel301: TLabel;    AnchorLabel400: TLabel;
    //Einige der Linklabels (einige heißen auch labelx, da es eigentlich keinen
    //großen Vorteil hat, sie umzubenennen)
    jumpLabel: TLabel;    jumpLabel1_0: TLabel;    jumpLabel1_1: TLabel;
    jumpLabel1_2: TLabel;    jumpLabel1_3: TLabel;    jumpLabel2: TLabel;    Image4: TImage;
    //Hilfetexte
    Label1: TLabel;    Label10: TLabel;    Label11: TLabel;    Label12: TLabel;
    Label13: TLabel;    Label14: TLabel;    Label15: TLabel;    Label16: TLabel;
    Label17: TLabel;    Label18: TLabel;    Label19: TLabel;    Label2: TLabel;
    Label20: TLabel;    Label21: TLabel;    Label22: TLabel;    Label23: TLabel;
    Label24: TLabel;    Label25: TLabel;    Label26: TLabel;    Label27: TLabel;
    Label28: TLabel;    Label29: TLabel;    Label3: TLabel;    Label30: TLabel;
    Label31: TLabel;    Label32: TLabel;    Label33: TLabel;    Label34: TLabel;
    Label35: TLabel;    Label36: TLabel;    Label37: TLabel;    Label38: TLabel;
    Label39: TLabel;    Label4: TLabel;    Label40: TLabel;    Label41: TLabel;
    Label42: TLabel;    Label43: TLabel;    Label44: TLabel;    Label45: TLabel;
    Label46: TLabel;    Label47: TLabel;    Label48: TLabel;    Label49: TLabel;
    Label5: TLabel;    Label50: TLabel;    Label51: TLabel;    Label52: TLabel;
    Label53: TLabel;    Label54: TLabel;    Label55: TLabel;    Label56: TLabel;
    Label57: TLabel;    Label58: TLabel;    Label59: TLabel;    Label6: TLabel;
    Label60: TLabel;    Label61: TLabel;    Label62: TLabel;    Label64: TLabel;
    Label65: TLabel;    Label7: TLabel;    Label8: TLabel;    Label9: TLabel;
    Panel1: TPanel;    Panel2: TPanel;
    //Panelbuttons, die nach Anklick andere Komponenten markieren
    //(Normale Buttons können nicht zweizeilig beschriftet werden)
    PanelButton1: TPanel;    PanelButton2: TPanel;    PanelButton3: TPanel;
    PanelButton4: TPanel;    PanelButton5: TPanel;    PanelButton6: TPanel;
    PanelButton7: TPanel;    PanelButton8: TPanel;    PanelButtonText1: TLabel;
    PanelButtonText2: TLabel;    PanelButtonText3: TLabel;    PanelButtonText4: TLabel;
    PanelButtonText5: TLabel;    PanelButtonText6: TLabel;    PanelButtonText7: TLabel;
    PanelButtonText8: TLabel;
    Label63: TLabel;
    //Panelbutton gedrückt zeichnen (Bevel <= bvLowered)
    procedure PanelButtonText1MouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    //Panelbutton normal zeichnen (Bevel <= bvRaised)
    procedure PanelButtonText1MouseUp(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    //Panelbutton entweder gedrückt oder normal zeichnen, je nach Mausstatus
    procedure PanelButtonText1MouseMove(Sender: TObject;
      Shift: TShiftState; X, Y: Integer);
    //Klick auf Panelbutton (jetzt werden andere Komponenten markiert bzw. demarkiert)
    procedure markerClick(Sender: TObject);

    procedure FormCreate(Sender: TObject); //Berechnung der Titelleistenbuttons
    procedure ListBox1Click(Sender: TObject); //Themenauswahl
    procedure jumpLabelClick(Sender: TObject); //Sprung zum anchorLabel
    procedure FormClose(Sender: TObject; var Action: TCloseAction); //Schließen
    //Hilfe zur Kompoenente an (x,y) (Bildschirmkoordinaten) aufrufen
    procedure helperMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    //Vor dem Beenden nachfragen, wenn die Hilfe zum 2D-Modus nicht gelesen wurde
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    //Beenden
    procedure Button1Click(Sender: TObject);
    //Anzeigen, dass man nicht auf die Bilder klicken soll
    procedure ImageClick(Sender: TObject);
  private
    { Private-Deklarationen}
    firstStart:boolean; //Wurde die Hilfe zum ersten Mal geöffnet?
  public
    { Public-Deklarationen}
  end;
  //Jedes Control hat eine versteckten Fonteigenschaft, wenn man es dann in eine
  //passende Klasse mit öffentlichem Font konvertiert, kann man darauf zugreifen.
  TFontControl=class (TControl)
    published
      property font;
  end;
var
  help: Thelp;

const
  htHelp = htSizeLast + 1;
  htTip = htSizeLast + 2;
var
  //Position des Tipbuttons, bei den verschiedenen Borderstyles,
  //der Hilfebutton hat die selben Y-Koordinaten
  buttonTipRects: array[0..5] of TRect;
  button2Width:integer; //Größe des Hilfebuttons
//Feststellen welcher Button (htHelp/htTip) auf dem Titelleiste der form an der
//Stelle (x,y) ist
function getButtonAt(form:TForm;x,y:integer):integer;
//Es wurde auf die Titelleiste der form geklickt, entweder wird die Hilfe geöffnet
//oder der Tipmodus eingeschalten
procedure clickedAt(form:TForm;x,y:integer);
//Zeichnet die Buttons in der Titelleist von form, pressedButton wird gedrückt
//gezeichnet
procedure drawButton(form:TForm;pressedButton:integer);


implementation

uses a1_u,threeD;

{$R *.DFM}

//Feststellen welcher Button (htHelp/htTip) auf dem Titelleiste der form an der
//Stelle (x,y) ist
function getButtonAt(form:TForm;x,y:integer):integer;
var area:trect;
    tip:PRect;
begin
  result:=0;
  GetWindowRect(form.handle,area); //Position der Form in Bildschirmkoordinaten
  //Position des Tipbuttons in Koordinaten Formkoordinaten bezüglich der Rahmenecke
  tip:=@buttonTipRects[integer(form.borderStyle)];

  if (y-area.top >  tip.top) and (y-area.top <  tip.bottom) then //Stimmt Y?
    if x < area.right+tip.right then //Links vom rechten Rand des Tipbutton?
      if x > area.right+tip.left then //Rechts vom Hilfebutton?
        result:=htTip //Dann ist es der Tipbutton
      else if x > area.right+tip.left-button2Width then //Rechts vom linken Rand des Hilfebuttons?
        result:=htHelp; //Dann ist es dieser
end;

//Es wurde auf die Titelleiste der form geklickt, entweder wird die Hilfe geöffnet
//oder der Tipmodus eingeschalten
procedure clickedAt(form:TForm;x,y:integer);
var gotten:integer;
begin
  gotten:=getButtonAt(form,x,y); //Wo wurde hingeklickt?
  case gotten of
    htHelp: with help do begin //Hilfebutton?
              ListBox1.ItemIndex:=form.tag; //Form als Thema nehmen
              ListBox1Click(nil); //Thema auswählen
              ShowModal; //Hilfe aufrufen
            end;
    htTip: begin //Tipbutton
             Screen.Cursor:=crHelp; //Fragezeichen als Mauszeiger
             setcapture(help.helper.Handle); //Nächste Klickposition an helper senden
           end;
  end;
end;

//Zeichnet die Buttons in der Titelleist von form, pressedButton wird gedrückt
//gezeichnet
procedure drawButton(form:TForm;pressedButton:integer);
var dc:HDC;
    rect,area:TRect;
    canvas:TCanvas;
begin
  GetWindowRect(form.handle,area);//Windowsgröße feststellen
  dc:=getwindowdc(form.handle); //Zeichenfläche holen

  //Y-Position der Buttons übernehmen
  rect.top:=buttonTipRects[integer(form.borderStyle)].top;
  rect.bottom:=buttonTipRects[integer(form.borderStyle)].bottom;
  //X-Position hängt von der Fenstergröße ab
  rect.left:=area.right-area.left+buttonTipRects[integer(form.borderStyle)].left;
  rect.Right:=area.right-area.left+buttonTipRects[integer(form.borderStyle)].right;

  //Tipbutton zeichnen
  if pressedButton=htTip then
    DrawFrameControl(dc,rect,DFC_CAPTION,DFCS_CAPTIONHELP or DFCS_PUSHED)
  else DrawFrameControl(dc,rect,DFC_CAPTION,DFCS_CAPTIONHELP);

  //Canvas zur Schriftdarstellung erzeugen
  canvas:=TCanvas.create;
  canvas.handle:=dc;
  canvas.brush.style:=bsClear;
  canvas.font.color:=clMaroon;
  canvas.font.style:=[fsBold];
  button2Width:=canvas.TextWidth('HILFE')+4; //Breite des Buttons

  //Rechte Seite des Hilfebuttons ist die linke des Tipbuttons
  rect.right:=rect.left;
  //Linke Seite ein Stück versetzt zeichnen
  rect.left:=rect.left-button2Width;
  //Hilfebutton zeichnen
  if pressedButton=htHelp then
    DrawFrameControl(dc,rect,DFC_BUTTON,DFCS_BUTTONPUSH or DFCS_PUSHED)
  else DrawFrameControl(dc,rect,DFC_BUTTON,DFCS_BUTTONPUSH);

  //Beschriftung ausgeben
  case form.BorderStyle of
    //Dünne Titelleiste
    bsToolWindow,bsSizeToolWin: canvas.TextRect(rect,rect.left+2,rect.top-2,'HILFE');
    //Dicke Titelleiste
    else canvas.TextRect(rect,rect.left+2,rect.top,'HILFE');
  end;

  //Zeichenflächen freigeben
  canvas.free;
  ReleaseDC(form.handle, dc);
end;

//Lässt die Komponente in controls blinken indem sie blinkCount-mal für blinkTime
//rot und dann wieder schwarz gezeichnet wird. Ist markedAtEnd gesetzt, werden
//sie am Ende nochmal rot markiert
procedure Blink(const blinkCount,blinkTime:integer;const markedAtEnd:boolean; const controls:array of tcontrol);
  //Kopiert die Schriftart vom ersten Kontrol auf alle anderen
  procedure copy;
  var i:integer;
  begin
    for i:=1 to high(controls) do
      tfontcontrol(controls[i]).font:=tfontcontrol(controls[0]).font;
  end;
  //Zeichnet sie rot
  procedure mark;
  begin
    //Setzt die Schriftart des ersten auf rot
    with tfontcontrol(controls[0]).font do begin
      Style:=[fsBold];
      color:=clRed;
    end;
    //Ist das Kontrol ein Shape, wird es zusätzlich sichtbar gemacht
    if controls[0] is tshape then tshape(controls[0]).visible:=true;
    copy; //Kopiert die Schriftart auf alle anderen
  end;
  //Zeichnet sie normal
  procedure demark;
  begin
    //Setzt die Schriftart des ersten zurück
    with tfontcontrol(controls[0]).font do begin
      Style:=[];
      color:=clBlack;
    end;
    //Ist das Kontrol ein Shape, wird es zusätzlich unsichtbar gemacht
    if controls[0] is tshape then tshape(controls[0]).visible:=false;
    copy;//Kopiert die Schriftart auf alle anderen
  end;
var i:integer;
begin
  for i:=1 to blinkCount do begin //blinkcount mal
    mark; //rot
    Application.ProcessMessages; //Aktualisieren
    sleep(blinkTime); //warten
    demark; //normal
    Application.ProcessMessages; //Aktualisieren
    if i<>blinkCount then sleep(blinkTime);//nur am Schluss nicht warten
  end;
  if markedAtEnd then begin //Am Schluss markieren?
    Application.ProcessMessages; //nochmal markieren
    sleep(blinkTime);
    mark;
  end;
end;

//Panelbutton gedrückt zeichnen (Bevel <= bvLowered)
procedure Thelp.PanelButtonText1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var panelButton:tpanel; //welcher Panelbutton?
begin
  panelButton:=findComponent('PanelButton'+INttoStr(abs(tcontrol(sender).tag))) as TPanel;
  panelButton.BevelInner:=bvLowered;
  panelButton.BevelOuter:=bvLowered;
end;

//Panelbutton normal zeichnen (Bevel <= bvRaised)
procedure Thelp.PanelButtonText1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var panelButton:tpanel; //welcher Panelbutton?
begin
  panelButton:=findComponent('PanelButton'+INttoStr(abs(tcontrol(sender).tag))) as TPanel;
  panelButton.BevelInner:=bvRaised;
  panelButton.BevelOuter:=bvRaised;
end;

//Panelbutton entweder gedrückt oder normal zeichnen, je nach Mausstatus
procedure Thelp.PanelButtonText1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var panelButton:tpanel; //welcher Panelbutton?
    senderC:tcontrol; //Angeklickte Komponente (PanelButton selbst oder Text)
    lowered:boolean;
begin
  if not (ssLeft in shift) then exit; //Nur weiter, wenn eine Taste gedrückt ist

  senderC:=tcontrol(sender);
  panelButton:=findComponent('PanelButton'+INttoStr(abs(tcontrol(sender).tag))) as TPanel;

            //Beim Button muss die Maus drin sein
  lowered:= ((sender=panelButton) and (x>0) and (y>0)
                 and (x<panelButton.width) and (y<panelButton.height)) or
            //Bei der Beschriftung, muss die Position umgerechnet werden
            ((senderC.parent=panelButton) and (x>-senderC.left) and (y>-senderC.Top)
                 and (x<panelButton.width-senderC.left) and (y<panelButton.height-senderC.Top));

  //Innenrand zeichnen
  if lowered then   panelButton.BevelInner:=bvLowered
  else panelButton.BevelInner:=bvRaised;
  panelButton.BevelOuter:=panelButton.BevelInner; //Außenrand entsprechend

end;

//Klick auf Panelbutton (jetzt werden andere Komponenten markiert bzw. demarkiert)
procedure Thelp.markerClick(Sender: TObject);
var panelButton:tpanel; //welcher Panelbutton?
    panelButtonText:tlabel; //Beschriftung
    blinkCount:integer; //Wieoft sollen die Komponenten blinken?

begin
  //Button und Text suchen
  panelButton:=findComponent('PanelButton'+INttoStr(abs(tcontrol(sender).tag))) as TPanel;
  panelButtonText:=findComponent('PanelButtonText'+INttoStr(abs(tcontrol(sender).tag))) as TLabel;

  case abs(panelButton.Tag) of
    1,2,3,4 : mainForm.BringToFront; //Markierung in der Hauptform (immer sichtbar)
    5,6,7,8: begin //Markierung in der 3D-Form
         if threeDForm.Visible then //sichtbar?
           threeDForm.BringToFront //dann einfach nach vorne holen
         else begin //unsichtbar?
           threeDForm.Show; //öffnen
           while not threeDForm.Visible do //OpenGL landen dauert...
             Application.ProcessMessages;
           Sleep(10); //nochmal kurz warten
         end;
       end;
  end;
  if panelButton.Tag>0 then //Markieren oder rückgängig
      blinkCount:=PRETTY_BLINK_COUNT //Beim markieren lange blinken
  else blinkCount:=2; //sonst nur kurz

  case abs(panelButton.Tag) of
    //Welche Komponenten sollen jeweils blinken?
    1:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [mainForm.createSpheres,mainForm.sphereCount]);
    2:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [mainForm.saveSize,mainForm.save,mainForm.fileName,mainForm.selectFileName]);
    3:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [mainForm.moveMarker,mainForm.size,mainForm.showAll]);
    4:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [mainForm.open3DMode]);
    5:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [threeDForm.newSphereCount, threeDForm.createSpheres]);
    6:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [threeDForm.save,threeDForm.filename,threeDForm.selectFile]);
    7:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [threeDForm.showAll,threeDForm.rotationX,threeDForm.rotationY,threeDForm.size]);
    8:  Blink(blinkCount,PRETTY_BLINK_TIME,panelButton.Tag>0,
              [threeDForm.markShape]);

  end;

  panelButton.Tag:=-panelButton.tag; //Nächstes mal die gegenteilige Funktion ausführen
  //Beschriftung und Hilfe ändern
  if panelButton.Tag<0 then begin
    panelButtonText.Caption:='Klicken Sie hier, wenn sie die Stellen im Programm nicht mehr markiert haben wollen';
    panelButtonText.hint:='Wenn Sie auf diesen Knopf klicken, wird die Hilfe für sehr kurze Zeit ausgeblendet, '+
                          'und die an dieser Stelle beschriebenen Steuerelemente blinken kurz rot.'#13#10+
                          'Dann wird die Hilfe wieder eingeblendet, während die Steuerelement wieder normal werden.';
  end else begin
    panelButtonText.Caption:='Klicken Sie hier, wenn sie die Stellen im Programm markiert haben wollen';
    panelButtonText.hint:='Wenn Sie auf diesen Knopf klicken, wird die Hilfe für kurze Zeit ausgeblendet, '+
                          'und die an dieser Stelle beschriebenen Steuerelemente blinken solange rot.'#13#10+
                          'Dann wird die Hilfe wieder eingeblendet, während die Steuerelement rot bleiben.';
  end;
  panelButton.hint:=panelButtonText.hint;
  BringToFront; //wieder die Hilfe anzeigen
end;


//Berechnung der Titelleistenbuttons und des TPanels
procedure Thelp.FormCreate(Sender: TObject);
var caption, smallCaption: integer;
    smallButtonX, ButtonX: integer;
    fixedFrameX, sizeFrameX: integer;
    smallButtonY, ButtonY: integer;
    fixedFrameY, sizeFrameY: integer;
    //ncmetrics:TNonClientMetrics;
begin
  firstStart:=true; //demmnächst kommt der erste Hilfestart

  //Panel wird an die linke obere Bildschirmecke geschickt
  //damit sind die lokalen Panelkoordinaten, exakt die Bildschirmkoordinaten
  windows.setparent(helper.Handle,GetDesktopWindow);
  helper.left:=0;
  helper.top:=0;

  //Fenstergrößen auslesen
  fixedFrameX:=GetSystemMetrics(SM_CXFIXEDFRAME);
  sizeFrameX:=GetSystemMetrics(SM_CXSIZEFRAME);
  fixedFrameY:=GetSystemMetrics(SM_CXFIXEDFRAME);
  sizeFrameY:=GetSystemMetrics(SM_CXSIZEFRAME);
  ButtonX:=GetSystemMetrics(SM_CXSIZE)-2;
  smallButtonX:=GetSystemMetrics(SM_CXSMSIZE)-3;
  ButtonY:=GetSystemMetrics(SM_CYSIZE)-4;
  smallButtonY:=GetSystemMetrics(SM_CYSMSIZE)-4;
  caption:=GetSystemMetrics(SM_CYCAPTION);
  smallcaption:=GetSystemMetrics(SM_CYSMCAPTION);
  //Daraus jeweils die Buttonpositionen berechnen
  with buttonTipRects[integer(bsDialog)] do begin
    //Rahmenbreite + Button zentriert auf der Leiste
    top:=fixedFrameY+(caption-ButtonY) div 2;
    //Unterseite = Oberseite + Höhe
    bottom:=top + ButtonY;
    //Rechteseite = - (Rahmen + ungefähre Größe der anderen Buttons)
    right:=-(fixedFrameX+2*ButtonX);
    //Linkeseite = Rechte - Breite
    left:=right-ButtonX;
  end;
  with buttonTipRects[integer(bsNone)] do; //Keine Titelleiste
  with buttonTipRects[integer(bsSingle)] do begin //wie oben
    top:=buttonTipRects[integer(bsDialog)].top;
    bottom:=buttonTipRects[integer(bsDialog)].bottom;
    right:=-(fixedFrameX+4*ButtonX);
    left:=right-ButtonX;
  end;
  with buttonTipRects[integer(bsSizeable)] do begin//wie oben
    top:=sizeFrameY+(caption-ButtonY) div 2;
    bottom:=sizeFrameY+(caption+ButtonY) div 2;
    right:=-(sizeFrameX+4*ButtonX);
    left:=right-ButtonX;
  end;
  with buttonTipRects[integer(bsSizeToolWin)] do begin//wie oben
    top:=sizeFrameY+(smallcaption-smallButtonY) div 2;
    bottom:=sizeFrameY+(smallcaption+smallButtonY) div 2;
    right:=-(sizeFrameX+2*smallButtonX);
    left:=right-smallButtonX;
  end;
  with buttonTipRects[integer(bsToolWindow)] do begin//wie oben
    top:=fixedFrameY+(smallcaption-smallButtonY) div 2;
    bottom:=fixedFrameY+(smallcaption+smallButtonY) div 2;
    right:=-(fixedFrameX+2*smallButtonX);
    left:=right-smallButtonX;
  end;
end;

//Thema auswählen
procedure Thelp.ListBox1Click(Sender: TObject);
begin
  firstStart:=false; //Jetzt wurde eine andere Hilfeseite gelesen
  //Text nach vorne holen
  tcontrol(findComponent('Text'+IntToStr(ListBox1.ItemIndex))).BringToFront;
end;

//Zum anchorLabel hüpfen
procedure Thelp.jumpLabelClick(Sender: TObject);
var aim:TLabel; //Ziel
begin
  //Ziel finden
  aim:=findcomponent('anchorLabel'+inttostr(tcontrol(sender).tag)) as tlabel;
  //Hinscrollen
  TScrollBox(aim.parent).VertScrollBar.Position:=aim.top-5;
end;

procedure Thelp.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  firstStart:=false; //Der erste Start ist zu Ende
end;

//Auf das (unsichtbare) Tippanel geklickt
procedure Thelp.helperMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var window:tcontrol; //Auf welche Komponente wurde wirklich geklickt?
    i:integer; //for-Schleife
    pos:tpoint; //Mausposition
begin
  Screen.Cursor:=crDefault; //Normaler Mauszeiger
  window:=FindVCLWindow(point(x,y)); //Finde das echte Ziel
  if window.ShowHint then ShowMessage(window.hint) //Hilfe dazu anzeigen
  else begin //Keine Hilfe vorhanden? Das ist schlecht
    //Wurde die falsche Komponente gefunden? (FindVCLWindow sucht nämlich nur nach
    //TWinControls, PaintBoxen sind aber von TGraphicControl(und das von TControl)
    //abgeleiten
    for i:=0 to window.ComponentCount-1 do
      if window.Components[i] is tcontrol then
        //Hat das überhaupt ein Hilfe?
        if (tcontrol(window.Components[i]).showHint) then begin
          //Bildschirmkoordinaten => Lokale Koordinaten von components[i]
          pos:=tcontrol(window.Components[i]).ScreenToClient(point(x,y));
          //Ist die Maus drin?
          if (pos.x>0) and (pos.y>0) and
           (pos.x<tcontrol(window.Components[i]).Width) and
           (pos.y<tcontrol(window.Components[i]).Height) 
            then begin
             window:=tcontrol(window.Components[i]); //Juhu, Hilfe gefunden
             break;
        end;
      end;

    if window.ShowHint then ShowMessage(window.hint) //Hilfe anzeigen
    else //oder auch nicht
      ShowMessage('Entschuldigung, aber leider ist keine Hilfe für diese Stelle vorhanden.'#13#10+
                  'Bitte versuchen Sie auch die Hilfe zu Steuerelement in der Nähe anzusehen');

  end;
end;

//Vor dem Beenden nachfragen, wenn beim ersten Start keine Hilfe zu einem anderen
//Thema gelesen wurde
procedure Thelp.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if firstStart then //Ist das so?
    if MessageBox(handle,'Wollen Sie die Hilfe wirklich verlassen?'#13#10'Sie haben '+
                         'nämlich noch nicht die Informationen über den 2D-Modus '+
                         'gelesen, die Sie an der linken Fensterseite auswählen '+
                         'können.','Kugelsimulator',MB_YESNO)<>ID_YES then begin
      //Gut der Benutzter will nicht beenden
      ListBox1.ItemIndex:=1; //2D-Modus-Hilfe auswählen
      ListBox1Click(nil); //öffnen
      CanClose:=false; //nicht beenden
     end;
end;

procedure Thelp.Button1Click(Sender: TObject);
begin
  close;
end;

procedure Thelp.ImageClick(Sender: TObject);
begin
  //Unglaublich, aber ich habe letzt erlebt wie jemand bei einem ähnlichen Hilfe-
  //system tatsächlich auf die Beschreibungsbilder geklickt hat.
  ShowMessage('Entschuldigung, aber die Bilder dienen nur zur Information, wo man '+
              'im Programm klicken soll, und haben keine weitere Funktion.'#13#10+
              'Wollen Sie die angegebene Funktion aufrufen, klicken Sie bitte im Programm '+
              'und nicht in der Hilfe auf die hier beschriebene Funktion.');
end;

end.
