unit comp_browser_main;

{$mode objfpc}{$H+}

interface

uses
	Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, Menus,
	Buttons, StdCtrls, ExtCtrls, ActnList, MaskEdit, grids, CheckLst, PairSplitter,
	ColorBox, ValEdit, SynHighlighterPosition, strutils, SynEdit, DateTimePicker,
	PopupNotifier, ExtDlgs, ComboEx, ShellCtrls, PrintersDlgs, Spin, EditBtn,
	FileCtrl, Arrow, XMLPropStorage, IniPropStorage, JsonPropStorage,
	IDEWindowIntf, ButtonPanel, typinfo;

type
	TPalettePage = (
		ppStandard,
		ppAdditional,
		ppCommonControls,
		ppDialogs,
		ppMisc,
		ppOtherPagesNotListedHere
	);
	{ TForm1 }

 TForm1 = class(TForm)
		seViewer: TSynEdit;
		tvComps: TTreeView;
		procedure FormCreate(Sender: TObject);
		procedure tvCompsChange(Sender: TObject; Node: TTreeNode);
	private
		compClass: TComponentClass;
		Hiliter: TSynPositionHighlighter;
		atrUL, atrBD: TtkTokenKind;
		lineNo: integer;
		procedure LoadTreeView;
		procedure DisplayComponentInfo(aNode: TTreeNode);
		procedure DisplayPageInfo(aNode: TTreeNode);
		procedure DisplayComponentData;
		procedure DisplayComponentHierarchy(aNode: TTreeNode);
		procedure DisplayComponentProperties;
		function GetAncestorCount(aClass: TClass): integer;
		function GetComponentClass(aPage: TPalettePage; anIndex: word): TComponentClass;
	public

	end;

 const
	 MaxComponentsOnAPage = 21; // AdditionalPage
	 PageNames : array[TPalettePage] of shortstring =
		 ('Standard', 'Additional', 'Common Controls', 'Dialogs', 'Misc', 'Other unlisted pages');

var
	Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
	hiliter := TSynPositionHighlighter.Create(Self);
	seViewer.Highlighter := hiliter;
	atrUL := hiliter.CreateTokenID('atrUL', clBlue, clNone, [fsBold, fsUnderline]);
	atrBD := hiliter.CreateTokenID('atrBD', clBlack, clNone, [fsBold]);
	LoadTreeView;
end;

procedure TForm1.tvCompsChange(Sender: TObject; Node: TTreeNode);
begin
	DisplayComponentInfo(Node);
end;

procedure TForm1.LoadTreeView;
var aNode: TTreeNode;
	palPage: TPalettePage;
	i: integer;
begin
	tvComps.BeginUpdate;
	for palPage := High(TPalettePage) downto Low(TPalettePage) do
		begin
			aNode := tvComps.Items.AddFirst(nil, PageNames[palPage]);
			for i := 1 to MaxComponentsOnAPage do
				begin
					compClass := GetComponentClass(palPage, i);
					if Assigned(compClass) then
						tvComps.Items.AddChildObject(
							aNode,
							compClass.ClassName,
							TObject(compClass)
						);
				end;
		end;

	tvComps.EndUpdate;
	tvComps.Selected := tvComps.Items[0];
end;

procedure TForm1.DisplayComponentInfo(aNode: TTreeNode);
begin
	seViewer.Lines.Clear;
	hiliter.ClearAllTokens;
	lineNo := 0;

	case aNode.Level of
		0: begin
			seViewer.Lines.Add('');
			seViewer.Lines.Add(' (' + aNode.Text + ' Page)');
		end
		else
			begin
				compClass := TComponentClass(aNode.Data);
				DisplayPageInfo(aNode);
				DisplayComponentData;
				DisplayComponentHierarchy(aNode);
				DisplayComponentProperties;
			end;
	end;
end;

procedure TForm1.DisplayPageInfo(aNode: TTreeNode);
var s: string;
begin
	seViewer.Lines.Add('');
	inc(lineNo);

	s := ' Palette Page: ' + aNode.Parent.Text;

	hiliter.AddToken(lineNo, 1, tkText);
	hiliter.AddToken(lineNo, Length(s), atrUL);

	seViewer.Lines.Add(s);
	inc(lineNo);

	seViewer.Lines.Add('');
	inc(lineNo);
end;

procedure TForm1.DisplayComponentData;
var st: string;
begin
	st := ' ' + compClass.ClassName;

	HiLiter.AddToken(lineNo, 1, tkText);
	HiLiter.AddToken(lineNo, Length(st), atrUL);

	seViewer.Lines.Add(st);
	inc(lineNo);

	seViewer.Lines.Add('');
	inc(lineNo);

	seViewer.Lines.Add(
		Format(' ''%s'' is declared in the %s unit', [
			compClass.ClassName,
			compClass.UnitName
		])
	);
	inc(lineNo);

	seViewer.Lines.Add(
		Format(' InstanceSize is : %d bytes', [compClass.InstanceSize])
	);
	inc(lineNo);

	seViewer.Lines.Add('');
	inc(lineNo);
end;

procedure TForm1.DisplayComponentHierarchy(aNode: TTreeNode);
var
	sl: TStringList;
	step: integer = 1;
	ancestorCount : integer = 0;
	i: integer;
	s: string;
	aClass: TClass;

	function Plural(aCount: integer): string;
	begin
		case aCount of
			1: result := '';
			else result := 'es';
		end;
	end;

begin
	ancestorCount := GetAncestorCount(compClass);
	s := Format(
		' %s class hierarchy [%d ancestor class%s]',
		[compClass.ClassName, ancestorCount, Plural(ancestorCount)]
	);

	hiliter.AddToken(lineNo, 1, tkText);
	hiliter.AddToken(lineNo, Length(s), atrBD);
	seViewer.Lines.Add(s);
	inc(lineNo);

	aClass := TClass(aNode.Data);

	if Assigned(aClass.ClassParent) then
		begin
			sl := TStringList.Create;
			try
				while Assigned(aClass.ClassParent) do
					begin
						sl.Add(DupeString(' ', step) + aClass.ClassName);
						aClass := aClass.ClassParent;
						inc(step, 2);
					end;

				sl.Add(DupeString(' ', step) + aClass.ClassName);
				for i := sl.Count -1 downto 0 do
					begin
						seViewer.Lines.Add(sl[i]);
						inc(lineNo);
					end;
			finally
				sl.Free;
			end;
		end
	else
		begin
			seViewer.Lines.Add('    (No parent class)');
			inc(lineNo);
		end;
end;

procedure TForm1.DisplayComponentProperties;
var
	aPPI: PPropInfo;
	aPTI: PTypeInfo;
	aPTD: PTypeData;
	aPropList: PPropList;
	sortSL: TStringList;
	i: integer;
	s: string;
begin
	seViewer.Lines.Add('');
	inc(LineNo);

	aPTI := PTypeInfo(compClass.ClassInfo);
	aPTD := GetTypeData(aPTI);
	s := Format(
		' %s has %d published properties:',
		[aPTI^.Name, aPTD^.PropCount]
	);

	hiliter.AddToken(lineNo, 1, tkText);
	hiliter.AddToken(lineNo, Length(s), atrBD);

	seViewer.Lines.Add(s);
	inc(lineNo);

	if (aPTD^.PropCount = 0)
		then seViewer.Lines.Add('    (no published properties)')
	else
		begin
			GetMem(aPropList, SizeOf(PPropInfo^) * aPTD^.PropCount);
			sortSL := TStringList.Create;
			sortSL.Sorted := true;

			try
				GetPropInfos(aPTI, aPropList);
				for i := 0 to aPTD^.PropCount - 1 do
					begin
						aPPI := aPropList^[i];
						sortSL.AddObject(Format(
							' %s: %s',
							[aPPI^.Name, aPPI^.PropType^.Name]
						), TObject(Pointer(Length(aPPI^.Name))));
					end;

				for i := 0 to sortSL.Count - 1 do
					begin
						seViewer.Lines.Add(sortSL[i]);
						hiliter.AddToken(lineNo, Succ(Integer(Pointer(sortSL.Objects[i]))), atrBD);
						hiliter.AddToken(lineNo, Length(sortSL[i]), tkText);
						inc(lineNo);
					end;
			finally
				FreeMem(aPropList, SizeOf(PPropInfo) * aPTD^.PropCount);
				sortSL.Free;
			end;
		end;
end;

function TForm1.GetAncestorCount(aClass: TClass): integer;
begin
	result := 0;
	if not Assigned(aClass.ClassParent)
		then Exit
	else
		begin
			while Assigned(aClass.ClassParent) do
				begin
					inc(result);
					aClass := aClass.ClassParent;
				end;
		end;
end;

function TForm1.GetComponentClass(
	aPage: TPalettePage;
	anIndex: word
): TComponentClass;
begin
	case aPage of
		ppStandard: case anIndex of
			1: result := TMainMenu;
			2: result := TPopupMenu;
			3: result := TButton;
			4: result := TLabel;
			5: result := TEdit;
			6: result := TMemo;
			7: result := TToggleBox;
			8: result := TCheckBox;
			9: result := TRadioButton;
			10: result := TListBox;
			11: result := TComboBox;
			12: result := TScrollBar;
			13: result := TGroupBox;
			14: result := TRadioGroup;
			15: result := TCheckGroup;
			16: result := TPanel;
			17: result := TFrame;
			18: result := TActionList;
			else result := nil;
		end;

		ppAdditional: case anIndex of
			1: result := TBitBtn;
			2: result := TSpeedbutton;
			3: result := TStaticText;
			4: result := TImage;
			5: result := TShape;
			6: result := TBevel;
			7: result := TPaintBox;
			8: result := TNotebook;
			9: result := TlabeledEdit;
			10: result := TSplitter;
			11: result := TTrayIcon;
			12: result := TMaskEdit;
			13: result := TCheckListBox;
			14: result := TScrollBox;
			15: result := TApplicationProperties;
			16: result := TStringGrid;
			17: result := TDrawGrid;
			18: result := TPairSplitter;
			19: result := TColorBox;
			20: result := TColorListBox;
			21: result := TValueListEditor;
			else result := nil;
		end;

		ppCommonControls: case anIndex of
			1: result := TTrackBar;
			2: result := TProgressBar;
			3: result := TTreeView;
			4: result := TListView;
			5: result := TStatusBar;
			6: result := TToolBar;
			7: result := TCoolBar;
			8: result := TUpDown;
			9: result := TPageControl;
			10: result := TTabControl;
			11: result := THeaderControl;
			12: result := TImageList;
			13: result := TPopupNotifier;
			14: result := TDateTimePicker;
			else result := nil;
		end;

		ppDialogs: case anIndex of
			1: result := TOpenDialog;
			2: result := TSaveDialog;
			3: result := TSelectDirectoryDialog;
			4: result := TColorDialog;
			5: result := TFontDialog;
			6: result := TFindDialog;
			7: result := TReplaceDialog;
			8: result := TTaskDialog;
			9: result := TOpenPictureDialog;
			10: result := TSavePictureDialog;
			11: result := TCalendarDialog;
			12: result := TCalculatorDialog;
			13: result := TPrinterSetupDialog;
			14: result := TPrintDialog;
			15: result := TPageSetupDialog;
			else result := nil;
		end;

		ppMisc: case anIndex of
			1: result := TColorButton;
			2: result := TSpinEdit;
			3: result := TFloatSpinEdit;
			4: result := TArrow;
			5: result := TEditButton;
			6: result := TFileNameEdit;
			7: result := TDirectoryEdit;
			8: result := TDateEdit;
			9: result := TTimeEdit;
			10: result := TCalcEdit;
			11: result := TFileListBox;
			12: result := TFilterComboBox;
			13: result := TComboBoxEx;
			14: result := TCheckComboBox;
			15: result := TButtonPanel;
			16: result := TShellTreeView;
			17: result := TShellListView;
			18: result := TXMLPropStorage;
			19: result := TIniPropStorage;
			20: result := TJsonPropStorage;
			21: result := TIDEDialogLayoutStorage;
			else result := nil;
		end;

		else result := nil;
	end;
end;

end.

