unit Unit7;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, Unit3, strLib, ImgList, SynEdit, SynMemo,
  Menus;

type
  TFrmHelp = class(TForm)
    StatusBar1: TStatusBar;
    Panel1: TPanel;
    Splitter3: TSplitter;
    Panel2: TPanel;
    ImageList1: TImageList;
    Panel3: TPanel;
    Panel4: TPanel;
    Panel5: TPanel;
    Panel6: TPanel;
    mainTree: TTreeView;
    Panel7: TPanel;
    Panel8: TPanel;
    Panel9: TPanel;
    Panel10: TPanel;
    synmemo: TSynEdit;
    PopupMenu1: TPopupMenu;
    Copy1: TMenuItem;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure mainTreeChange(Sender: TObject; Node: TTreeNode);
    procedure AddMemoLine(s: String; relinkon: String; fore: TColor; back: TColor);
    procedure synmemoSpecialLineColors(Sender: TObject; Line: Integer;
      var Special: Boolean; var FG, BG: TColor);
    procedure Copy1Click(Sender: TObject);
    procedure synmemoClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    function FirstCase(s: String): String;
  protected
    Procedure CreateParams(var Params :TCreateParams); override;
  public
    procedure LoadHelp();
    procedure SelectCommand(comm: String);
    { Public declarations }
  end;

// treeview data record per gli help
THelpNode = class
  syntax, param, desc, seealso, example: TStringList;
  family, command: String;
  special_node: Boolean;
end;

var
  FrmHelp: TFrmHelp;
  // string list usata per colorare le righe
  rowscol: TStringList;
  rowscolfont: TStringList;
  // string list che ci dice dove rimanda la riga se ci clicco sopra
  seealsolist: TStringList;

implementation

uses Unit1;

{$R *.dfm}

procedure TFrmHelp.AddMemoLine(s: String; relinkon: String; fore: TColor; back: TColor);
begin
  synmemo.Lines.Add(s);
  rowscol.Add(ColorToString(back));
  rowscolfont.Add(ColorToString(fore));
  seealsolist.Add(relinkon);
end;

procedure TFrmHelp.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.ExStyle := Params.ExStyle OR WS_EX_APPWINDOW;
  Params.WndParent := GetDesktopWindow;
end;

procedure TFrmHelp.SelectCommand(comm: String);
var
b, a: Integer;
padre: TTreeNode;
helpNode: THelpNode;
begin
  for a:=0 to mainTree.Items.Count-1 do begin
    padre := mainTree.Items[a];
    // i dati sono nei nodi figli
    for b := 0 to padre.Count-1 do begin
      helpNode := padre.Item[b].Data;
      // trovato, seleziona
      if UpperCase(helpNode.command) = UpperCase(comm) then begin
        padre.Item[b].Selected:=true;
        exit;
      end;
    end;
  end;
end;

procedure TFrmHelp.FormClose(Sender: TObject; var Action: TCloseAction);
var
b, a: Integer;
padre: TTreeNode;
helpNode: THelpNode;
begin
  // distrugge stringslist colorazione
  rowscol.Free;
  rowscolfont.Free;
  seealsolist.Free;
  // distruggi dati nei nodi
  for a:=0 to mainTree.Items.Count-1 do begin
    padre := mainTree.Items[a];
    // i dati sono nei nodi figli
    for b := 0 to padre.Count-1 do begin
      helpNode := padre.Item[b].Data;
      helpnode.syntax.Free;
      helpnode.param.Free;
      helpnode.desc.Free;
      helpnode.seealso.Free;
      helpnode.example.Free;
      helpnode.Free;
    end;
  end;
  // distruggi form
  Action:=caFree;
  MainForm.SetNilHelpForm();
end;

procedure TFrmHelp.LoadHelp();
var
  ResData: PChar;
  ResSize: Longword;
  command, res, tag: String;
  lines: TStringList;
  a,b: Integer;
  helpNode: THelpNode;
  searchFam, searchSyn, searchPar, searchDesc, searchAlso, searchExample: Boolean;
  famexists: Integer;
  newnode, famnode: TTreeNode;
  foundSort: Boolean;
begin
  // --- STEP ZERO - CARICA I ROOT NODES DEL TREEVIEW
  mainTree.Items.BeginUpdate;
  lines := TstringList.Create;

  ResData := EGetResourceAsPointer(PChar('MAINHELPROOTNODES'), RT_RCDATA, ResSize);
  if ResData <> nil then begin
    SetString(res, ResData, ResSize);

    res := Replace(res, chr(10), '');
    res := Replace(res, chr(9), '');

    SplitStrings(res, chr(13), lines);
    a:=0;
    while a < lines.Count do begin
      mainTree.Items.Add(nil, lines.Strings[a]);
      a := a + 1;
    end;
  end;

  // --- FIRST STEP - LETTURA DAL FILE .H DEL LOADER


  // effettua il parsing delle risorse e riempie treeview e dati
  ResData := EGetResourceAsPointer(PChar('LOADERLUA'), RT_RCDATA, ResSize);
  if ResData <> nil then begin
    SetString(res, ResData, ResSize);

    res := Replace(res, chr(10), '');
    res := Replace(res, chr(9), '');

    SplitStrings(res, chr(13), lines);
    a:=0;
    while a < lines.Count do begin

      tag := '---- STARTSECTION:';

      if Pos(tag, lines.Strings[a]) > 0 then begin
        command := Copy(lines.Strings[a], Pos(tag, lines.Strings[a])+Length(tag), Length(lines.Strings[a]) - Pos(tag, lines.Strings[a])-Length(tag)+1);
        a := a + 1;
        tag := '---- ENDSECTION:';

        helpNode := THelpNode.Create;
        helpNode.syntax:=TStringList.Create;
        helpNode.param:=TStringList.Create;
        helpNode.desc:=TStringList.Create;
        helpNode.seealso:=TStringList.Create;
        helpNode.example:=TStringList.Create;
        helpNode.family:='unknown';
        helpNode.command := command;
        helpNode.special_node := false; // non e' un nodo speciale, riempiremo tutte le liste

        searchFam := false; searchSyn := false; searchPar := false; searchDesc := False; searchAlso := false; searchExample:=false;

        while Pos(tag, lines.Strings[a]) = 0 do begin

          if Pos('FAMILY:', lines.Strings[a]) > 0 then begin
            searchFam := true; searchSyn := false; searchPar := false; searchDesc := False; searchAlso := false; searchExample:=false;
          end else if Pos('SYNTAX:', lines.Strings[a]) > 0 then begin
            searchFam := false; searchSyn := true; searchPar := false; searchDesc := False; searchAlso := false; searchExample:=false;
          end else if Pos('PARAM:', lines.Strings[a]) > 0 then begin
            searchFam := false; searchSyn := false; searchPar := true; searchDesc := False; searchAlso := false; searchExample:=false;
          end else if Pos('DESCRIPTION:', lines.Strings[a]) > 0 then begin
            searchFam := false; searchSyn := false; searchPar := false; searchDesc := true; searchAlso := false; searchExample:=false;
          end else if Pos('SEEALSO:', lines.Strings[a]) > 0 then begin
            searchFam := false; searchSyn := false; searchPar := false; searchDesc := false; searchAlso := true; searchExample:=false;
          end else if Pos('EXAMPLE:', lines.Strings[a]) > 0 then begin
            searchFam := false; searchSyn := false; searchPar := false; searchDesc := false; searchAlso := false; searchExample:=true;
          end else begin
            if searchFam then helpNode.family := lines.Strings[a]
            else if searchSyn then helpNode.syntax.Add(lines.Strings[a])
            else if searchPar then helpNode.param.Add(lines.Strings[a])
            else if searchDesc then helpNode.desc.Add(lines.Strings[a])
            else if searchAlso then helpNode.seealso.Add(lines.Strings[a])
            else if searchExample then helpNode.example.Add(lines.Strings[a]);
          end;

          a:=a+1;
        end;

        // verifica se la famiglia c'è già...
        famexists := -1;
        for b:=0 to mainTree.Items.Count -1 do begin
          if UpperCase(mainTree.Items[b].Text) = UpperCase(helpNode.family) then begin
            famexists := b;
            break;
          end;
        end;

        // se la famiglia non esiste, creala...
        if famexists = -1 then begin
          mainTree.Items.AddNode(nil, nil, FirstCase(helpNode.family), nil, naAdd	 );
          famexists := mainTree.Items.Count-1;
        end;

        // ok, aggiungi il comando a questa famiglia
        famnode := mainTree.Items[famexists];

        // cerca tra i figli, quello che alfabeticamente verrebbe dopo, e aggiungilo prima
        foundSort:=false;
        for b:=0 to famnode.Count-1 do begin
          // trovato dove inserire
          if FirstCase(famnode.Item[b].Text) >= FirstCase(command) then begin
            newnode := mainTree.Items.InsertObject(famnode.Item[b], FirstCase(command), helpNode);
            newnode.ImageIndex := 1;
            newnode.SelectedIndex := 1;
            foundSort:=true;
            break;
          end;
        end;

        // Se non l'hai trovato, metti li a muzzo
        if foundSort =false then begin
            newnode := mainTree.Items.AddChildObject(famnode, FirstCase(command), helpNode);
            newnode.ImageIndex := 1;
            newnode.SelectedIndex := 1;
        end;

      end;
      a:=a+1;
    end; // ciclo linee

  end;

  // --- SECOND STEP - LETTURA DAL FILE HELP.TXT NELLE RISORSE
  lines.Clear;
  ResData := EGetResourceAsPointer(PChar('HELPFBA'), RT_RCDATA, ResSize);
  if ResData <> nil then begin
    SetString(res, ResData, ResSize);

    res := Replace(res, chr(10), '');
    res := Replace(res, chr(9), '');

    SplitStrings(res, chr(13), lines);
    a:=0;
    while a < lines.Count do begin

      // salta i commenti che iniziano con <-->
      if Pos('<-->', lines.Strings[a]) > 0 then begin
        a := a + 1;
        continue;
      end;

      tag := '---- STARTSECTION:';

      if Pos(tag, lines.Strings[a]) > 0 then begin
        command := Copy(lines.Strings[a], Length(tag)+1, Length(lines.Strings[a])-Length(tag));

        helpNode := THelpNode.Create;
        helpNode.syntax:=TStringList.Create;
        helpNode.param:=TStringList.Create;
        helpNode.desc:=TStringList.Create;
        helpNode.seealso:=TStringList.Create;
        helpNode.example:=TStringList.Create;
        helpNode.family:=Copy(command, 1, Pos('||', command)-1);
        helpNode.command := Copy(command, Pos('||', command)+2, Length(command)-Pos('||', command));
        helpNode.special_node := true; // e' un nodo speciale, riempiremo solo desc

        a := a + 1;

        // adesso aggiungiamo il testo a desc fino al prossimo trigger
        // o alla fine delle righe
        while ( (a < lines.Count) and (Pos(tag, lines.Strings[a]) = 0 ) ) do begin
          // salta i commenti che iniziano con <-->
          if Pos('<-->', lines.Strings[a]) = 0 then helpNode.desc.Add(lines.Strings[a]);
          a := a + 1;
        end;

        // verifica se la famiglia c'è già...
        famexists := -1;
        for b:=0 to mainTree.Items.Count -1 do begin
          if UpperCase(mainTree.Items[b].Text) = UpperCase(helpNode.family) then begin
            famexists := b;
            break;
          end;
        end;

        // se la famiglia non esiste, creala...
        if famexists = -1 then begin
          mainTree.Items.AddNode(nil, nil, FirstCase(helpNode.family), nil, naAdd	);
          famexists := mainTree.Items.Count-1;
        end;

        // ok, aggiungi il comando a questa famiglia
        famnode := mainTree.Items[famexists];

        // cerca tra i figli, quello che alfabeticamente verrebbe dopo, e aggiungilo prima
        // maio - gli help non dei comandi, li mostro nell'ordine in cui sono scritti nella risorsa
        foundSort:=false;
        if false then begin
        for b:=0 to famnode.Count-1 do begin
          // trovato dove inserire
          if FirstCase(famnode.Item[b].Text) >= FirstCase(helpNode.command) then begin
            newnode := mainTree.Items.InsertObject(famnode.Item[b], FirstCase(helpNode.command), helpNode);
            newnode.ImageIndex := 1;
            newnode.SelectedIndex := 1;
            foundSort:=true;
            break;
          end;
        end;
        end;

        // Se non l'hai trovato, metti li a muzzo
        if foundSort =false then begin
            newnode := mainTree.Items.AddChildObject(famnode, helpNode.command, helpNode);
            newnode.ImageIndex := 1;
            newnode.SelectedIndex := 1;
        end;
      end;

    end;
  end;

  lines.Free;

  mainTree.Items.EndUpdate;

  synmemo.Text := '';
end;


function TFrmHelp.FirstCase(s: String): String;
begin
  if Length(s) > 1 then begin
    Result := UpperCase(Copy(s, 1, 1)) + LowerCase(Copy(s, 2, Length(s)-1));
  end else begin
    Result := UpperCase(s);
  end;
end;

procedure TFrmHelp.mainTreeChange(Sender: TObject; Node: TTreeNode);
var
b, a, c: Integer;
padre: TTreeNode;
helpNode: THelpNode;
wannaexit: Boolean;
begin
  // resetta stringslist per colorazione righe
  rowscol.Clear;
  rowscolfont.Clear;
  seealsolist.Clear;
  // cerca elemento selezionato
  wannaexit := false;
  synmemo.Lines.BeginUpdate;
  synmemo.Clear;
  for a:=0 to mainTree.Items.Count-1 do begin
    padre := mainTree.Items[a];
    // i dati sono nei nodi figli
    for b := 0 to padre.Count-1 do begin
      // trovato il nodo selezionato! rullllez
      if padre.Item[b].Selected then begin
        // riempi l'help
        helpNode := padre.Item[b].Data;

        if helpNode.special_node = false then begin
          // RIEMPI NODO NORMALE CON COMMAND REFERENCE DAL FILE .H
          AddMemoLine('Command', '', synmemo.Font.Color, $00663500);
          AddMemoLine(helpNode.command, '', synmemo.Font.Color, synmemo.Color);

          AddMemoLine('', '', synmemo.Font.Color, synmemo.Color);
          AddMemoLine('Syntax', '', synmemo.Font.Color, $00663500);
          for c:=0 to helpNode.syntax.Count-1 do begin
            AddMemoLine(helpNode.syntax.Strings[c], '', synmemo.Font.Color, synmemo.Color);
          end;

          if helpNode.param.Count > 0 then begin
            AddMemoLine('', '', synmemo.Font.Color, synmemo.Color);
            AddMemoLine('Parameters', '', synmemo.Font.Color, $00663500);
            for c:=0 to helpNode.param.Count-1 do begin
              AddMemoLine(helpNode.param.Strings[c], '', synmemo.Font.Color, synmemo.Color);
            end;
          end;

          AddMemoLine('', '', synmemo.Font.Color, synmemo.Color);
          AddMemoLine('Description', '', synmemo.Font.Color, $00663500);
          for c:=0 to helpNode.desc.Count-1 do begin
            AddMemoLine(helpNode.desc.Strings[c], '', synmemo.Font.Color, synmemo.Color);
          end;

          if helpNode.seealso.Count > 0 then begin
            AddMemoLine('', '', synmemo.Font.Color, synmemo.Color);
            AddMemoLine('See Also (click to jump)', '', synmemo.Font.Color, $00663500);
            for c:=0 to helpNode.seealso.Count-1 do begin
              AddMemoLine(helpNode.seealso.Strings[c],
                helpNode.seealso.Strings[c], $0049B459, synmemo.Color);
            end;
          end;

          if helpNode.example.Count > 0 then begin
            AddMemoLine('', '', synmemo.Font.Color, synmemo.Color);
            AddMemoLine('Example', '', synmemo.Font.Color, $00663500);
            for c:=0 to helpNode.example.Count-1 do begin
              AddMemoLine(helpNode.example.Strings[c], '', synmemo.Font.Color, synmemo.Color);
            end;
          end;

        end else begin
          // RIEMPI NODO SPECIALE CON HELP DENTRO DESC
          for c := 0 to helpNode.desc.Count-1 do begin

            if Copy(helpNode.desc.Strings[c], 1, 3) = '[B]' then begin
              AddMemoLine(
                Copy(helpNode.desc.Strings[c], 4, Length(helpNode.desc.Strings[c])-3),
                '', synmemo.Font.Color, $00663500);
            end else begin
              AddMemoLine(helpNode.desc.Strings[c], '', synmemo.Font.Color, synmemo.Color);
            end;

          end;
        end;

        wannaexit := true;
        break;
        //
      end;
    end;
    if wannaexit then break;
  end;
  synmemo.Lines.EndUpdate;
end;

procedure TFrmHelp.synmemoSpecialLineColors(Sender: TObject; Line: Integer;
  var Special: Boolean; var FG, BG: TColor);
begin
  if Line > rowscol.Count then exit;
  // colora la linea
  BG :=StringToColor( rowscol.Strings[Line-1] );
  FG :=StringToColor( rowscolfont.Strings[Line-1] );
end;

procedure TFrmHelp.Copy1Click(Sender: TObject);
begin
  synmemo.CopyToClipboard;
end;

procedure TFrmHelp.synmemoClick(Sender: TObject);
var
mouse: TBufferCoord;
begin
  // verifica se è una linea "see also"
  synmemo.GetPositionOfMouse(mouse);
  if mouse.Line > seealsolist.Count then exit;

  // salta all'argomento linkato
  if Length(seealsolist.Strings[mouse.Line-1]) > 0 then begin
    SelectCommand(seealsolist.Strings[mouse.Line-1]);
  end;

end;

procedure TFrmHelp.FormCreate(Sender: TObject);
begin
  // on top
  SetWindowPos(
    self.Handle,
    HWND_TOPMOST,
    0,0,0,0,
    SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
  // crea strings list per colorazione
  rowscol := TStringList.Create;
  rowscolfont := TStringList.Create;
  seealsolist := TStringList.Create;
end;

end.
