zydis/assets/InstructionEditor/Forms/formMain.pas

1602 lines
48 KiB
Plaintext

unit formMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.UITypes,
System.ImageList, System.Generics.Collections, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.ImgList, Vcl.StdCtrls, Vcl.ExtCtrls, dxSkinsCore, dxSkinBlue, dxSkinSeven, dxDockPanel, cxOI,
dxSkinsdxBarPainter, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters,cxVGrid,
dxRibbonCustomizationForm, dxRibbonSkins, cxStyles, cxEdit, cxInplaceContainer, dxSkinsForm,
dxStatusBar, dxRibbonStatusBar, cxClasses, dxRibbon, dxBar, dxRibbonForm, cxSplitter, cxPC,
dxBarExtItems, dxSkinsdxDockControlPainter, dxDockControl, dxSkinsdxRibbonPainter,
dxGDIPlusClasses, VirtualTrees, Zydis.InstructionEditor;
// TODO: Add support for multi node selection and allow copy / paste / cut / delete of mutiple
// definitions
// http://www.delphipraxis.net/136601-virtual-treeview-multiselect-onchange-event-problem.html
// TODO: [ ] Update inspector after inspected object changed
// [ ] Create seperated class for persistent settings
// [ ] Seperate view from business logic
type
TfrmMain = class(TdxRibbonForm)
BarManager: TdxBarManager;
RibbonTab1: TdxRibbonTab;
Ribbon: TdxRibbon;
StatusBar: TdxRibbonStatusBar;
SkinController: TdxSkinController;
barMainManu: TdxBar;
barEditor: TdxBar;
lbLoadDatabase: TdxBarLargeButton;
lbSaveDatabase: TdxBarLargeButton;
imgIcons16: TcxImageList;
imgIcons32: TcxImageList;
lbCreateDefinition: TdxBarLargeButton;
barStatusBarProgress: TdxBar;
piStatusBarProgress: TdxBarProgressItem;
barView: TdxBar;
Splitter: TcxSplitter;
bbDuplicateDefinition: TdxBarButton;
bbDeleteDefinition: TdxBarButton;
barTools: TdxBar;
lbCodeGenerator: TdxBarLargeButton;
pnlInspector: TPanel;
DockingManager: TdxDockingManager;
imgMisc: TcxImageList;
DockSite: TdxDockSite;
LayoutDockSite: TdxLayoutDockSite;
pnlPropertyInspector: TdxDockPanel;
Inspector: TcxRTTIInspector;
pnlPropertyInformation: TdxDockPanel;
VertContainerDockSite: TdxVertContainerDockSite;
lblPropertyInfo: TLabel;
popupEditor: TdxRibbonPopupMenu;
dxBarSeparator1: TdxBarSeparator;
dxBarSeparator2: TdxBarSeparator;
dxBarSeparator3: TdxBarSeparator;
bbClipboardCopy: TdxBarButton;
barClipboard: TdxBar;
lbClipboardPaste: TdxBarLargeButton;
bbClipboardCut: TdxBarButton;
lbMnemonicFilter: TdxBarLargeButton;
bbExpandNodes: TdxBarButton;
bbCollapseNodes: TdxBarButton;
barMnemonicFilter: TdxBar;
edtMnemonicFilter: TdxBarEdit;
bbExactMatch: TdxBarButton;
EditorTree: TVirtualStringTree;
imgTreeView: TcxImageList;
dxBarSeparator4: TdxBarSeparator;
bbExpandLeaf: TdxBarButton;
bbCollapseLeaf: TdxBarButton;
lbDiffingMode: TdxBarLargeButton;
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure lbLoadDatabaseClick(Sender: TObject);
procedure lbSaveDatabaseClick(Sender: TObject);
procedure EditorTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
TextType: TVSTTextType; var CellText: string);
procedure EditorTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure EditorTreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
Column: TColumnIndex; var Result: Integer);
procedure EditorTreeCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode;
var Allowed: Boolean);
procedure EditorTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure lbCreateDefinitionClick(Sender: TObject);
procedure lbCodeGeneratorClick(Sender: TObject);
procedure bbDeleteDefinitionClick(Sender: TObject);
procedure InspectorItemChanged(Sender: TObject; AOldRow: TcxCustomRow; AOldCellIndex: Integer);
procedure bbDuplicateDefinitionClick(Sender: TObject);
procedure EditorTreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure bbClipboardCopyClick(Sender: TObject);
procedure lbClipboardPasteClick(Sender: TObject);
procedure bbExpandNodesClick(Sender: TObject);
procedure bbCollapseNodesClick(Sender: TObject);
procedure bbClipboardCutClick(Sender: TObject);
procedure lbMnemonicFilterClick(Sender: TObject);
procedure edtMnemonicFilterCurChange(Sender: TObject);
procedure bbExactMatchClick(Sender: TObject);
procedure EditorTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
procedure bbExpandLeafClick(Sender: TObject);
procedure bbCollapseLeafClick(Sender: TObject);
procedure EditorTreeGetImageIndex(Sender: TBaseVirtualTree;
Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
var Ghosted: Boolean; var ImageIndex: System.UITypes.TImageIndex);
procedure lbDiffingModeClick(Sender: TObject);
strict private
FFilename: String;
FEditor: TInstructionEditor;
FUpdating: Boolean;
FHasUnsavedChanges: Boolean;
FExpandedFilterProperties: TList<String>;
FExpandedDefinitionProperties: TList<String>;
FInspectorActiveFilterRow: String;
FInspectorActiveDefinitionRow: String;
FEditing: Boolean;
FEditedNode: PVirtualNode;
strict private
procedure EditorWorkStart(Sender: TObject; MinWorkCount, MaxWorkCount: Integer);
procedure EditorWork(Sender: TObject; WorkCount: Integer);
procedure EditorWorkEnd(Sender: TObject);
procedure EditorBeginUpdate(Sender: TObject);
procedure EditorEndUpdate(Sender: TObject);
procedure EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter);
procedure EditorFilterInserted(Sender: TObject; Filter: TInstructionFilter);
procedure EditorFilterChanged(Sender: TObject; Filter: TInstructionFilter);
procedure EditorFilterRemoved(Sender: TObject; Filter: TInstructionFilter);
procedure EditorFilterDestroyed(Sender: TObject; Filter: TInstructionFilter);
procedure EditorDefinitionCreated(Sender: TObject; Definition: TInstructionDefinition);
procedure EditorDefinitionInserted(Sender: TObject; Definition: TInstructionDefinition);
procedure EditorDefinitionChanged(Sender: TObject; Definition: TInstructionDefinition);
procedure EditorDefinitionRemoved(Sender: TObject; Definition: TInstructionDefinition);
procedure EditorDefinitionDestroyed(Sender: TObject; Definition: TInstructionDefinition);
strict private
function GetTreeNode(const Definition: TInstructionDefinition): PVirtualNode; overload;
function GetTreeNode(const Filter: TInstructionFilter): PVirtualNode; overload;
strict private
procedure SetDefaultWindowPosition; inline;
procedure LoadGUIConfiguration;
procedure SaveGUIConfiguration;
procedure UpdateExpandedProperties;
strict private
procedure UpdateControls;
procedure UpdateStatistic;
strict private
procedure ClipboardPaste(Node: PVirtualNode);
procedure ClipboardCopy(Node: PVirtualNode);
procedure ClipboardCut(Node: PVirtualNode);
procedure DefinitionCreate;
procedure DefinitionDuplicate(Node: PVirtualNode);
procedure DefinitionDelete(Node: PVirtualNode);
procedure ExpandAllNodes(Expanded: Boolean);
procedure ExpandLeaf(Node: PVirtualNode; Expanded: Boolean);
procedure SetMnemonicFilter(const Filter: String; ExactMatch: Boolean; DiffingMode: Boolean);
public
{ Public-Deklarationen }
end;
var
frmMain: TfrmMain;
implementation
uses
System.IniFiles, Vcl.Clipbrd, SynCrossPlatformJSON, formCreateDefinition, formCodeGenerator,
untHelperClasses, untPropertyHints;
{$R *.dfm}
type
TEditorNodeType = (
ntFilterTable,
ntInstructionDefinition
);
TDiffingState = (
dsDefault,
dsAdded,
dsRemoved
);
PEditorNodeData = ^TEditorNodeData;
TEditorNodeData = record
public
NodeType: TEditorNodeType;
DiffingState: TDiffingState;
case Integer of
0: (DataClass: TPersistent);
1: (Filter: TInstructionFilter);
2: (Definition: TInstructionDefinition);
end;
{$REGION 'Code: TreeView related methods'}
function TfrmMain.GetTreeNode(const Definition: TInstructionDefinition): PVirtualNode;
begin
// We are using the "data" property to store the corresponding node pointer
Assert(Assigned(Definition.Data));
Result := Definition.Data;
end;
function TfrmMain.GetTreeNode(const Filter: TInstructionFilter): PVirtualNode;
begin
// We are using the "data" property to store the corresponding node pointer
Assert(Assigned(Filter.Data));
Result := Filter.Data;
end;
{$ENDREGION}
{$REGION 'Code: TreeView related operations'}
procedure TfrmMain.ClipboardCopy(Node: PVirtualNode);
procedure SaveToJSON(Filter: TInstructionFilter; JSONArray: PJSONVariantData);
var
I: Integer;
JSONObject: TJSONVariantData;
begin
if (Filter.IsDefinitionContainer) then
begin
for I := 0 to (Filter as TDefinitionContainer).DefinitionCount - 1 do
begin
JSONObject.Init;
(Filter as TDefinitionContainer).Definitions[I].SaveToJSON(@JSONObject);
JSONArray^.AddValue(Variant(JSONObject));
end;
end else
begin
for I := 0 to Filter.Capacity - 1 do
begin
if (Assigned(Filter.Items[I])) then
begin
SaveToJSON(Filter.Items[I], JSONArray);
end;
end;
end;
end;
var
NodeData: PEditorNodeData;
JSON,
JSONArray,
JSONObject: TJSONVariantData;
begin
NodeData := EditorTree.GetNodeData(Node);
if (Assigned(NodeData)) then
begin
JSONArray.Init;
if (NodeData^.NodeType = ntInstructionDefinition) then
begin
JSONObject.Init;
NodeData^.Definition.SaveToJSON(@JSONObject);
JSONArray.AddValue(Variant(JSONObject));
end else
begin
if (Application.MessageBox(
'You are trying to copy multiple definitions to clipboard. Do you want to continue?',
'Question', MB_ICONQUESTION or MB_YESNO) <> IdYes) then
begin
Exit;
end;
SaveToJSON(NodeData^.Filter, @JSONArray);
end;
JSON.Init;
JSON.AddNameValue('definitions', Variant(JSONArray));
Clipboard.AsText := TJSONHelper.JSONToString(@JSON);
end;
end;
procedure TfrmMain.ClipboardCut(Node: PVirtualNode);
begin
ClipboardCopy(Node);
DefinitionDelete(Node);
end;
procedure TfrmMain.ClipboardPaste(Node: PVirtualNode);
var
JSON: TJSONVariantData;
JSONArray: PJSONVariantData;
I: Integer;
D: TInstructionDefinition;
begin
JSON.Init;
if (JSON.FromJSON(Clipboard.AsText) and (JSON.Kind = jvObject)) then
begin
JSONArray := JSON.Data('definitions');
if (Assigned(JSONArray) and (JSONArray^.Kind = jvArray)) then
begin
if (JSONArray^.Count > 1) then
begin
if (Application.MessageBox(
'You are trying to paste multiple definitions from clipboard. Do you want to continue?',
'Question', MB_ICONQUESTION or MB_YESNO) <> IdYes) then
begin
Exit;
end;
end;
FEditor.BeginUpdate;
try
for I := 0 to JSONArray^.Count - 1 do
begin
D := FEditor.CreateDefinition('unnamed');
try
D.BeginUpdate;
try
D.Update;
D.LoadFromJSON(JSONVariantDataSafe(JSONArray^.Item[I], jvObject));
finally
D.EndUpdate;
end;
except
on E: Exception do
begin
D.Free;
Application.MessageBox(PChar(E.Message), 'Error', MB_ICONERROR);
end;
end;
end;
finally
FEditor.EndUpdate;
end;
end;
end;
end;
procedure TfrmMain.DefinitionCreate;
var
frmCreateDefinition: TfrmCreateDefinition;
D: TInstructionDefinition;
begin
frmCreateDefinition := TfrmCreateDefinition.Create(Application);
try
D := FEditor.CreateDefinition('unnamed');
D.BeginUpdate;
try
// Force initial position update to cause OnDefinitionInserted for new definitions with
// unchanged (position-relevant) properties.
D.Update;
frmCreateDefinition.Inspector.InspectedObject := D;
frmCreateDefinition.ShowModal;
finally
if (not frmCreateDefinition.Canceled) then D.EndUpdate;
end;
if (frmCreateDefinition.Canceled) then
begin
D.Free;
end;
finally
frmCreateDefinition.Free;
end;
end;
procedure TfrmMain.DefinitionDelete(Node: PVirtualNode);
var
NextNode: PVirtualNode;
NodeData: PEditorNodeData;
begin
NodeData := EditorTree.GetNodeData(Node);
if Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition) then
begin
NextNode := EditorTree.GetNextSibling(Node);
if (not Assigned(NextNode)) then
begin
NextNode := EditorTree.GetPreviousSibling(Node);
end;
NodeData^.Definition.Free;
if (Assigned(NextNode)) then
begin
EditorTree.FocusedNode := NextNode;
EditorTree.Selected[NextNode] := true;
end;
end;
end;
procedure TfrmMain.DefinitionDuplicate(Node: PVirtualNode);
var
frmCreateDefinition: TfrmCreateDefinition;
D: TInstructionDefinition;
NodeData: PEditorNodeData;
begin
NodeData := EditorTree.GetNodeData(Node);
if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then
begin
frmCreateDefinition := TfrmCreateDefinition.Create(Application);
try
D := FEditor.CreateDefinition('unnamed');
D.BeginUpdate;
try
// Force initial position update to cause OnDefinitionInserted for new definitions with
// unchanged (position-relevant) properties.
D.Update;
D.Assign(NodeData^.Definition);
frmCreateDefinition.Inspector.InspectedObject := D;
frmCreateDefinition.ShowModal;
finally
if (not frmCreateDefinition.Canceled) then D.EndUpdate;
end;
if (frmCreateDefinition.Canceled) then
begin
D.Free;
end;
finally
frmCreateDefinition.Free;
end;
end;
end;
procedure TfrmMain.ExpandAllNodes(Expanded: Boolean);
var
Node: PVirtualNode;
NodeData: PEditorNodeData;
begin
EditorTree.BeginUpdate;
try
Node := EditorTree.GetFirst;
while (Assigned(Node)) do
begin
NodeData := EditorTree.GetNodeData(Node);
if (Assigned(NodeData) and (NodeData^.NodeType = ntFilterTable) and
(Assigned(NodeData^.Filter)) and
(not (iffIsRootTable in NodeData^.Filter.FilterFlags))) then
begin
EditorTree.Expanded[Node] := Expanded;
end;
Node := EditorTree.GetNext(Node);
end;
finally
EditorTree.EndUpdate;
end;
end;
procedure TfrmMain.ExpandLeaf(Node: PVirtualNode; Expanded: Boolean);
begin
// TODO:
end;
{$ENDREGION}
{$REGION 'Events: Form'}
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
ID: Integer;
begin
CanClose := true;
if (FHasUnsavedChanges) then
begin
ID := Application.MessageBox('The current database have unsaved changes. Do you'
+ ' really want to exit?', 'Question', MB_ICONWARNING or MB_YESNO or MB_DEFBUTTON2);
CanClose := (ID = IdYes);
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
EditorTree.NodeDataSize := SizeOf(TEditorNodeData);
FExpandedFilterProperties := TList<String>.Create;
FExpandedDefinitionProperties := TList<String>.Create;
SetDefaultWindowPosition;
LoadGUIConfiguration;
StatusBar.Panels[1].Visible := false;
FEditor := TInstructionEditor.Create;
FEditor.OnWorkStart := EditorWorkStart;
FEditor.OnWork := EditorWork;
FEditor.OnWorkEnd := EditorWorkEnd;
FEditor.OnBeginUpdate := EditorBeginUpdate;
FEditor.OnEndUpdate := EditorEndUpdate;
FEditor.OnFilterCreated := EditorFilterCreated;
FEditor.OnFilterInserted := EditorFilterInserted;
FEditor.OnFilterChanged := EditorFilterChanged;
FEditor.OnFilterRemoved := EditorFilterRemoved;
FEditor.OnFilterDestroyed := EditorFilterDestroyed;
FEditor.OnDefinitionCreated := EditorDefinitionCreated;
FEditor.OnDefinitionInserted := EditorDefinitionInserted;
FEditor.OnDefinitionChanged := EditorDefinitionChanged;
FEditor.OnDefinitionRemoved := EditorDefinitionRemoved;
FEditor.OnDefinitionDestroyed := EditorDefinitionDestroyed;
FEditing := false;
FEditor.Reset;
FEditing := true;
ExpandAllNodes(true);
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
FEditing := false;
SaveGUIConfiguration;
ExpandAllNodes(false);
if (Assigned(FEditor)) then
begin
FEditor.Free;
end;
if (Assigned(FExpandedFilterProperties)) then
begin
FExpandedFilterProperties.Free;
end;
if (Assigned(FExpandedDefinitionProperties)) then
begin
FExpandedDefinitionProperties.Free;
end;
end;
procedure TfrmMain.FormResize(Sender: TObject);
begin
piStatusBarProgress.Width := barStatusBarProgress.Control.ClientWidth;
end;
{$ENDREGION}
{$REGION 'Events: InstructionEditor'}
procedure TfrmMain.EditorBeginUpdate(Sender: TObject);
begin
EditorTree.BeginUpdate;
FUpdating := true;
end;
procedure TfrmMain.EditorDefinitionChanged(Sender: TObject; Definition: TInstructionDefinition);
begin
EditorTree.RepaintNode(GetTreeNode(Definition));
UpdateStatistic;
if (FEditing) then
begin
if (not (csDestroying in ComponentState)) and (lbMnemonicFilter.Down) then
begin
SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down, lbDiffingMode.Down);
end;
FHasUnsavedChanges := true;
UpdateControls;
end;
end;
procedure TfrmMain.EditorDefinitionCreated(Sender: TObject; Definition: TInstructionDefinition);
var
Node: PVirtualNode;
NodeData: PEditorNodeData;
begin
Node := EditorTree.AddChild(nil);
Definition.Data := Node;
EditorTree.IsVisible[Node] := false;
NodeData := EditorTree.GetNodeData(Node);
NodeData^.NodeType := ntInstructionDefinition;
NodeData^.Definition := Definition;
UpdateStatistic;
end;
procedure TfrmMain.EditorDefinitionDestroyed(Sender: TObject; Definition: TInstructionDefinition);
begin
EditorTree.DeleteNode(GetTreeNode(Definition));
if (Inspector.InspectedObject = Definition) then
begin
Inspector.InspectedObject := nil;
end;
UpdateStatistic;
end;
procedure TfrmMain.EditorDefinitionInserted(Sender: TObject; Definition: TInstructionDefinition);
var
Node: PVirtualNode;
begin
Assert(Assigned(Definition.Parent));
Node := GetTreeNode(Definition);
EditorTree.IsVisible[Node] := true;
EditorTree.MoveTo(Node, GetTreeNode(Definition.Parent), amAddChildLast, false);
if (FEditing) then
begin
FEditedNode := Node;
FHasUnsavedChanges := true;
UpdateControls;
end;
end;
procedure TfrmMain.EditorDefinitionRemoved(Sender: TObject; Definition: TInstructionDefinition);
var
Node: PVirtualNode;
begin
Node := GetTreeNode(Definition);
EditorTree.IsVisible[Node] := false;
EditorTree.MoveTo(Node, nil, amInsertAfter, false);
if (FEditing) then
begin
if (EditorTree.FocusedNode = Node) then
begin
EditorTree.FocusedNode := nil;
EditorTree.Selected[Node] := false;
end;
FHasUnsavedChanges := true;
UpdateControls;
end;
end;
procedure TfrmMain.EditorEndUpdate(Sender: TObject);
begin
EditorTree.EndUpdate;
FUpdating := false;
if (FEditing) and Assigned(FEditedNode) then
begin
EditorTree.FocusedNode := FEditedNode;
EditorTree.Selected[FEditedNode] := true;
EditorTree.ScrollIntoView(FEditedNode, true);
FEditedNode := nil;
end;
UpdateStatistic;
end;
procedure TfrmMain.EditorFilterChanged(Sender: TObject; Filter: TInstructionFilter);
begin
EditorTree.RepaintNode(GetTreeNode(Filter));
end;
procedure TfrmMain.EditorFilterCreated(Sender: TObject; Filter: TInstructionFilter);
var
Node: PVirtualNode;
NodeData: PEditorNodeData;
begin
Node := EditorTree.AddChild(nil);
Filter.Data := Node;
if (not (iffIsRootTable in Filter.FilterFlags)) then
begin
EditorTree.IsVisible[Node] := false;
end;
NodeData := EditorTree.GetNodeData(Node);
NodeData^.NodeType := ntFilterTable;
NodeData^.Filter := Filter;
UpdateStatistic;
end;
procedure TfrmMain.EditorFilterDestroyed(Sender: TObject; Filter: TInstructionFilter);
begin
EditorTree.DeleteNode(GetTreeNode(Filter));
if (Inspector.InspectedObject = Filter) then
begin
Inspector.InspectedObject := nil;
end;
UpdateStatistic;
end;
procedure TfrmMain.EditorFilterInserted(Sender: TObject; Filter: TInstructionFilter);
var
Node, ParentNode: PVirtualNode;
begin
Assert(Assigned(Filter.Parent));
Node := GetTreeNode(Filter);
ParentNode := GetTreeNode(Filter.Parent);
EditorTree.MoveTo(Node, ParentNode, amAddChildLast, false);
EditorTree.IsVisible[Node] := true;
// Expand root table after first filter insertion
if (iffIsRootTable in Filter.Parent.FilterFlags) and (Filter.Parent.ItemCount = 1) then
begin
EditorTree.Expanded[ParentNode] := true;
end;
end;
procedure TfrmMain.EditorFilterRemoved(Sender: TObject; Filter: TInstructionFilter);
var
Node: PVirtualNode;
begin
Node := GetTreeNode(Filter);
EditorTree.IsVisible[Node] := false;
EditorTree.MoveTo(Node, nil, amInsertAfter, false);
if (FEditing) then
begin
if (EditorTree.FocusedNode = Node) then
begin
EditorTree.FocusedNode := nil;
EditorTree.Selected[Node] := false;
end;
UpdateControls;
end;
end;
procedure TfrmMain.EditorWork(Sender: TObject; WorkCount: Integer);
begin
piStatusBarProgress.Position := WorkCount;
if ((WorkCount mod piStatusBarProgress.Tag) = 0) then
begin
Application.ProcessMessages;
end;
end;
procedure TfrmMain.EditorWorkEnd(Sender: TObject);
begin
piStatusBarProgress.Visible := ivNever;
end;
procedure TfrmMain.EditorWorkStart(Sender: TObject; MinWorkCount, MaxWorkCount: Integer);
begin
piStatusBarProgress.Min := MinWorkCount;
piStatusBarProgress.Max := MaxWorkCount;
piStatusBarProgress.Tag := Round((MaxWorkCount - MinWorkCount) / 100) + 1;
piStatusBarProgress.Position := 0;
piStatusBarProgress.Visible := ivAlways;
end;
{$ENDREGION}
{$REGION 'Events: TreeView'}
procedure TfrmMain.EditorTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
NodeData: PEditorNodeData;
I: Integer;
begin
UpdateExpandedProperties;
Inspector.BeginUpdate;
try
if (Assigned(Inspector.FocusedRow)) then
begin
if (Inspector.InspectedObject is TInstructionFilter) then
begin
FInspectorActiveFilterRow :=
(Inspector.FocusedRow as TcxPropertyRow).PropertyEditor.GetName;
end else if (Inspector.InspectedObject is TInstructionDefinition) then
begin
FInspectorActiveDefinitionRow :=
(Inspector.FocusedRow as TcxPropertyRow).PropertyEditor.GetName;
end;
end;
Inspector.InspectedObject := nil;
NodeData := Sender.GetNodeData(Node);
if Assigned(NodeData) then
begin
Inspector.InspectedObject := NodeData^.DataClass;
for I := 0 to Inspector.Rows.Count - 1 do
begin
if ((NodeData^.NodeType = ntFilterTable) and FExpandedFilterProperties.Contains(
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) or
((NodeData^.NodeType = ntInstructionDefinition) and
FExpandedDefinitionProperties.Contains(
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) then
begin
Inspector.Rows[I].Expanded := true;
end;
if ((NodeData^.NodeType = ntFilterTable) and (FInspectorActiveFilterRow =
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) or
((NodeData^.NodeType = ntInstructionDefinition) and
(FInspectorActiveDefinitionRow =
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName)) then
begin
Inspector.FocusedRow := Inspector.Rows[I];
end;
end;
end;
finally
Inspector.EndUpdate;
end;
UpdateControls;
end;
procedure TfrmMain.EditorTreeCollapsing(Sender: TBaseVirtualTree; Node: PVirtualNode;
var Allowed: Boolean);
var
NodeData: PEditorNodeData;
begin
NodeData := Sender.GetNodeData(Node);
if (Assigned(NodeData) and (NodeData^.NodeType = ntFilterTable) and
Assigned(NodeData^.Filter) and (iffIsRootTable in NodeData^.Filter.FilterFlags)) then
begin
Allowed := false;
end;
end;
procedure TfrmMain.EditorTreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
Column: TColumnIndex; var Result: Integer);
var
NodeDataA,
NodeDataB: PEditorNodeData;
begin
NodeDataA := Sender.GetNodeData(Node1);
NodeDataB := Sender.GetNodeData(Node2);
if (NodeDataA^.NodeType <> NodeDataB^.NodeType) then Exit;
if (Assigned(NodeDataA) and Assigned(NodeDataB) and
Assigned(NodeDataA^.DataClass) and Assigned(NodeDataB^.DataClass)) then
begin
case NodeDataA^.NodeType of
ntFilterTable:
begin
Assert(NodeDataB^.NodeType = ntFilterTable);
if (Assigned(NodeDataA^.Filter.Parent)) then
begin
Assert(Assigned(NodeDataB^.Filter.Parent));
Assert(NodeDataA^.Filter.Parent = NodeDataB^.Filter.Parent);
Result := NodeDataA^.Filter.Parent.IndexOf(NodeDataA^.Filter) -
NodeDataB^.Filter.Parent.IndexOf(NodeDataB^.Filter);
end;
end;
ntInstructionDefinition:
begin
Assert(NodeDataB^.NodeType = ntInstructionDefinition);
Result := CompareStr(NodeDataA^.Definition.Mnemonic, NodeDataB^.Definition.Mnemonic);
if (Result = 0) then
begin
Result := Ord(NodeDataA^.DiffingState) - Ord(NodeDataB^.DiffingState);
end;
end;
end;
end;
end;
procedure TfrmMain.EditorTreeGetImageIndex(Sender: TBaseVirtualTree;
Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
var Ghosted: Boolean; var ImageIndex: System.UITypes.TImageIndex);
var
NodeData: PEditorNodeData;
begin
if (Column <> 0) or (not (Kind in [ikNormal, ikSelected])) then
begin
Exit;
end;
NodeData := Sender.GetNodeData(Node);
if Assigned(NodeData) then
begin
case NodeData^.NodeType of
ntFilterTable:
begin
ImageIndex := 0;
if (NodeData^.Filter is TDefinitionContainer) then
begin
ImageIndex := 1;
end;
end;
ntInstructionDefinition:
begin
case NodeData^.DiffingState of
dsDefault: ImageIndex := 2;
dsAdded : ImageIndex := 3;
dsRemoved: ImageIndex := 4;
end;
end;
end;
end;
end;
procedure TfrmMain.EditorTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
NodeData: PEditorNodeData;
S: String;
begin
CellText := '';
NodeData := Sender.GetNodeData(Node);
if (Assigned(NodeData) and Assigned(NodeData^.DataClass)) then
begin
case (NodeData^.NodeType) of
ntFilterTable:
begin
if (TextType <> ttNormal) and (not (Column in [0])) then Exit;
case Column of
0:
begin
case TextType of
ttNormal:
begin
if (not Assigned(NodeData^.Filter.Parent)) then
begin
CellText := 'Root';
end else
begin
CellText := IntToHex(NodeData^.Filter.Parent.IndexOf(NodeData^.Filter), 2);
end;
end;
ttStatic:
begin
if (Assigned(NodeData^.Filter.Parent)) then
begin
S := NodeData^.Filter.Parent.GetItemDescription(
NodeData^.Filter.Parent.IndexOf(NodeData^.Filter));
if (S <> '') then
begin
CellText := '(' + S + ')';
end;
end;
end;
end;
end;
end;
end;
ntInstructionDefinition:
begin
if (TextType <> ttNormal) and (not (Column in [0, 1])) then Exit;
case Column of
0:
begin
case TextType of
ttNormal: CellText := IntToHex(Node.Index, 2);
ttStatic: CellText := 'Definition';
end;
end;
1:
begin
case TextType of
ttNormal:
begin
CellText := IntToHex(NodeData^.Definition.Opcode, 2);
end;
ttStatic:
begin
CellText := ''; // TODO:
end
end;
end;
2: CellText := NodeData^.Definition.Mnemonic;
3: CellText := NodeData^.Definition.Operands.OperandA.GetDescription(true);
4: CellText := NodeData^.Definition.Operands.OperandB.GetDescription(true);
5: CellText := NodeData^.Definition.Operands.OperandC.GetDescription(true);
6: CellText := NodeData^.Definition.Operands.OperandD.GetDescription(true);
7: CellText := NodeData^.Definition.Operands.OperandE.GetDescription(true);
8: CellText := NodeData^.Definition.Comment;
end;
end;
end;
end;
end;
procedure TfrmMain.EditorTreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure CopyOperands;
var
NodeData: PEditorNodeData;
I: Integer;
S: String;
O: TInstructionOperand;
begin
NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode);
if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then
begin
S := '';
for I := 0 to NodeData^.Definition.Operands.OperandCount - 1 do
begin
O := NodeData^.Definition.Operands.Operands[I];
S := S + IntToStr(Integer(O.OperandType)) + ',' + IntToStr(Integer(O.Encoding)) + ',' +
IntToStr(Integer(O.AccessMode)) + ',';
end;
Clipboard.AsText := S;
end;
end;
procedure PasteOperands;
var
NodeData: PEditorNodeData;
A: TArray<String>;
I, J: Integer;
O: TInstructionOperand;
begin
NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode);
if (Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition)) then
begin
A := Clipboard.AsText.Split([',']);
if (Length(A) >= 15) then
begin
I := 0;
J := 0;
NodeData^.Definition.BeginUpdate;
try
while (J < 5) do
begin
O := NodeData^.Definition.Operands.Operands[J];
O.OperandType := TOperandType(StrToInt(A[I]));
O.Encoding := TOperandEncoding(StrToInt(A[I + 1]));
O.AccessMode := TOperandAccessMode(StrToInt(A[I + 2]));
Inc(I, 3);
Inc(J);
end;
finally
NodeData^.Definition.EndUpdate;
end;
end;
end;
end;
begin
if (ssCtrl in Shift) then
begin
case Key of
Ord('V'):
lbClipboardPaste.Click;
Ord('C'):
bbClipboardCopy.Click;
Ord('X'):
bbClipboardCut.Click;
Ord('F'):
lbMnemonicFilter.Click;
Ord('E'):
CopyOperands;
Ord('R'):
PasteOperands;
end;
end else if (Shift = []) then
begin
case Key of
VK_DELETE:
bbDeleteDefinition.Click;
end;
end;
end;
procedure TfrmMain.EditorTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
if (Button = mbRight) then
begin
popupEditor.PopupFromCursorPos;
end;
end;
procedure TfrmMain.EditorTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
var
NodeData: PEditorNodeData;
begin
NodeData := Sender.GetNodeData(Node);
if (Assigned(NodeData) and Assigned(NodeData^.DataClass)) then
begin
case NodeData^.NodeType of
ntFilterTable:
begin
if (NodeData^.Filter.HasConflicts) then
begin
TargetCanvas.Font.Color := clRed;
Exit;
end;
end;
ntInstructionDefinition:
begin
if (NodeData^.Definition.HasConflicts) then
begin
TargetCanvas.Font.Color := clRed;
Exit;
end;
end;
end;
end;
case Column of
0:
begin
case TextType of
ttNormal: ;
ttStatic: TargetCanvas.Font.Color := clGray;
end;
end;
1:
begin
case TextType of
ttNormal: ;
ttStatic: TargetCanvas.Font.Color := clGray;
end;
end;
end;
end;
{$ENDREGION}
procedure TfrmMain.bbClipboardCopyClick(Sender: TObject);
begin
ClipboardCopy(EditorTree.FocusedNode);
end;
procedure TfrmMain.bbClipboardCutClick(Sender: TObject);
begin
ClipboardCut(EditorTree.FocusedNode);
end;
procedure TfrmMain.bbDuplicateDefinitionClick(Sender: TObject);
begin
DefinitionDuplicate(EditorTree.FocusedNode);
end;
procedure TfrmMain.bbExactMatchClick(Sender: TObject);
begin
SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down, lbDiffingMode.Down);
end;
procedure TfrmMain.bbExpandLeafClick(Sender: TObject);
begin
ExpandLeaf(EditorTree.FocusedNode, true);
end;
procedure TfrmMain.bbExpandNodesClick(Sender: TObject);
begin
ExpandAllNodes(true);
end;
procedure TfrmMain.bbCollapseLeafClick(Sender: TObject);
begin
ExpandLeaf(EditorTree.FocusedNode, false);
end;
procedure TfrmMain.bbCollapseNodesClick(Sender: TObject);
begin
ExpandAllNodes(false);
end;
procedure TfrmMain.bbDeleteDefinitionClick(Sender: TObject);
begin
DefinitionDelete(EditorTree.FocusedNode);
end;
procedure TfrmMain.edtMnemonicFilterCurChange(Sender: TObject);
begin
// TODO: Filter is offsync, if the user leaves the edit by pressing ESC or focusing an other
// control
SetMnemonicFilter(edtMnemonicFilter.CurText, bbExactMatch.Down, lbDiffingMode.Down);
end;
procedure TfrmMain.lbClipboardPasteClick(Sender: TObject);
begin
ClipboardPaste(EditorTree.FocusedNode);
end;
procedure TfrmMain.lbCreateDefinitionClick(Sender: TObject);
begin
DefinitionCreate;
end;
procedure TfrmMain.lbCodeGeneratorClick(Sender: TObject);
var
frmGenerator: TfrmCodeGenerator;
begin
frmGenerator := TfrmCodeGenerator.Create(Application);
try
frmGenerator.Editor := FEditor;
frmGenerator.ShowModal;
finally
frmGenerator.Free;
end;
end;
procedure TfrmMain.lbDiffingModeClick(Sender: TObject);
var
OpenDialog: TOpenDialog;
Node: PVirtualNode;
NodeData: PEditorNodeData;
Editor: TInstructionEditor;
I, J: Integer;
B: Boolean;
D: TInstructionDefinition;
begin
if (lbDiffingMode.Down) then
begin
OpenDialog := TOpenDialog.Create(Application);
try
OpenDialog.Title := 'Open second instruction database';
OpenDialog.Filter := 'Instruction Database Files (*.json)|*.json';
OpenDialog.DefaultExt := 'json';
OpenDialog.InitialDir := ExtractFilePath(ParamStr(0));
if (not OpenDialog.Execute(WindowHandle)) then
begin
lbDiffingMode.Down := false;
Exit;
end;
EditorTree.BeginUpdate;
try
for I := 0 to FEditor.DefinitionCount - 1 do
begin
Node := GetTreeNode(FEditor.Definitions[I]);
NodeData := EditorTree.GetNodeData(Node);
NodeData^.DiffingState := dsRemoved;
end;
Editor := TInstructionEditor.Create;
try
Editor.OnWorkStart := EditorWorkStart;
Editor.OnWork := EditorWork;
Editor.OnWorkEnd := EditorWorkEnd;
try
Editor.LoadFromFile(OpenDialog.Filename);
{if (lbMnemonicFilter.Down) then
begin
SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down);
end;}
FEditing := false;
FEditor.BeginUpdate;
try
EditorWorkStart(Editor, 0, Editor.DefinitionCount);
for I := 0 to Editor.DefinitionCount - 1 do
begin
B := false;
for J := 0 to FEditor.DefinitionCount - 1 do
begin
if (Editor.Definitions[I].Equals(FEditor.Definitions[J])) then
begin
B := true;
Node := GetTreeNode(FEditor.Definitions[J]);
NodeData := EditorTree.GetNodeData(Node);
NodeData^.DiffingState := dsDefault;
Break;
end;
end;
if (not B) then
begin
D := FEditor.CreateDefinition(Editor.Definitions[I].Mnemonic);
D.Assign(Editor.Definitions[I]);
Node := GetTreeNode(D);
NodeData := EditorTree.GetNodeData(Node);
NodeData^.DiffingState := dsAdded;
end;
EditorWork(Editor, I + 1);
end;
EditorWorkEnd(Editor);
finally
FEditor.EndUpdate;
FEditing := true;
end;
except
on E: Exception do
begin
Application.MessageBox(PChar(E.Message), 'Error', MB_ICONERROR);
end;
end;
finally
Editor.Free;
end;
finally
EditorTree.EndUpdate;
end;
finally
OpenDialog.Free;
end;
end else
begin
EditorTree.BeginUpdate;
try
FEditing := false;
FEditor.BeginUpdate;
try
EditorWorkStart(FEditor, 0, FEditor.DefinitionCount);
J := 0;
for I := FEditor.DefinitionCount - 1 downto 0 do
begin
Node := GetTreeNode(FEditor.Definitions[I]);
NodeData := EditorTree.GetNodeData(Node);
case NodeData^.DiffingState of
dsAdded : NodeData^.Definition.Free;
dsRemoved: NodeData^.DiffingState := dsDefault;
end;
Inc(J);
EditorWork(FEditor, J);
end;
EditorWorkEnd(FEditor);
finally
FEditor.EndUpdate;
FEditing := true;
end;
finally
EditorTree.EndUpdate;
end;
end;
UpdateControls;
end;
procedure TfrmMain.lbLoadDatabaseClick(Sender: TObject);
var
ID: Integer;
OpenDialog: TOpenDialog;
begin
if (FHasUnsavedChanges) then
begin
ID := Application.MessageBox('Reloading the database will revert all unsaved changes. Do you'
+ ' really want to continue?', 'Question', MB_ICONWARNING or MB_YESNO or MB_DEFBUTTON2);
if (ID <> IdYes) then
begin
Exit;
end;
end;
OpenDialog := TOpenDialog.Create(Application);
try
OpenDialog.Filter := 'Instruction Database Files (*.json)|*.json';
OpenDialog.DefaultExt := 'json';
OpenDialog.InitialDir := ExtractFilePath(ParamStr(0));
if (not OpenDialog.Execute(WindowHandle)) then
begin
Exit;
end;
FFilename := OpenDialog.FileName;
finally
OpenDialog.Free;
end;
FEditing := false;
try
ExpandAllNodes(false);
FEditor.LoadFromFile(FFilename);
if (lbMnemonicFilter.Down) then
begin
SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down, lbDiffingMode.Down);
end;
except
on E: Exception do
begin
FFilename := '';
Application.MessageBox(PChar(E.Message), 'Error', MB_ICONERROR);
end;
end;
FEditing := true;
FHasUnsavedChanges := false;
UpdateControls;
end;
procedure TfrmMain.lbMnemonicFilterClick(Sender: TObject);
begin
StatusBar.Panels[1].Visible := lbMnemonicFilter.Down;
piStatusBarProgress.Width := barStatusBarProgress.Control.ClientWidth;
if (lbMnemonicFilter.Down) then
begin
SetMnemonicFilter(edtMnemonicFilter.Text, bbExactMatch.Down, lbDiffingMode.Down);
edtMnemonicFilter.SetFocus;
end else
begin
SetMnemonicFilter('', false, false);
end;
end;
procedure TfrmMain.lbSaveDatabaseClick(Sender: TObject);
var
SaveDialog: TSaveDialog;
begin
if (FFilename = '') then
begin
SaveDialog := TSaveDialog.Create(Application);
try
SaveDialog.Filter := 'Instruction Database Files (*.json)|*.json';
SaveDialog.DefaultExt := 'json';
SaveDialog.InitialDir := ExtractFilePath(ParamStr(0));
if (not SaveDialog.Execute(WindowHandle)) then
begin
Exit;
end;
FFilename := SaveDialog.FileName;
finally
SaveDialog.Free;
end;
end;
FEditor.SaveToFile(FFilename);
FHasUnsavedChanges := false;
UpdateControls;
end;
procedure TfrmMain.LoadGUIConfiguration;
var
Ini: TIniFile;
I: Integer;
A: TArray<String>;
begin
DockingManager.LoadLayoutFromIniFile(ChangeFileExt(ParamStr(0), 'Layout.ini'));
Ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
try
for I := 0 to EditorTree.Header.Columns.Count - 1 do
begin
EditorTree.Header.Columns[I].Width := Ini.ReadInteger('Editor',
Format('Col_%.2d_Width', [I]), EditorTree.Header.Columns[I].Width);
end;
A := Ini.ReadString('Inspector', 'ExpandedFilterProperties', '').Split([',']);
for I := Low(A) to High(A) do
begin
FExpandedFilterProperties.Add(A[I]);
end;
A := Ini.ReadString('Inspector', 'ExpandedDefinitionProperties', '').Split([',']);
for I := Low(A) to High(A) do
begin
FExpandedDefinitionProperties.Add(A[I]);
end;
pnlInspector.Width := Ini.ReadInteger('Inspector', 'Width', 364);
Inspector.OptionsView.RowHeaderWidth := Ini.ReadInteger('Inspector', 'RowHeaderWidth', 170);
finally
Ini.Free;
end;
end;
procedure TfrmMain.SaveGUIConfiguration;
var
Ini: TIniFile;
I: Integer;
S: String;
begin
DockingManager.SaveLayoutToIniFile(ChangeFileExt(ParamStr(0), 'Layout.ini'));
Ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
try
for I := 0 to EditorTree.Header.Columns.Count - 1 do
begin
Ini.WriteInteger('Editor', Format('Col_%.2d_Width', [I]), EditorTree.Header.Columns[I].Width);
end;
UpdateExpandedProperties;
S := '';
for I := 0 to FExpandedFilterProperties.Count - 1 do
begin
S := S + FExpandedFilterProperties[I];
if (I < FExpandedFilterProperties.Count - 1) then
begin
S := S + ',';
end;
end;
Ini.WriteString('Inspector', 'ExpandedFilterProperties', S);
S := '';
for I := 0 to FExpandedDefinitionProperties.Count - 1 do
begin
S := S + FExpandedDefinitionProperties[I];
if (I < FExpandedDefinitionProperties.Count - 1) then
begin
S := S + ',';
end;
end;
Ini.WriteString('Inspector', 'ExpandedDefinitionProperties', S);
Ini.WriteInteger('Inspector', 'Width', pnlInspector.Width);
Ini.WriteInteger('Inspector', 'RowHeaderWidth', Inspector.OptionsView.RowHeaderWidth);
finally
Ini.Free;
end;
end;
procedure TfrmMain.SetDefaultWindowPosition;
var
R: TRect;
begin
R := Screen.MonitorFromPoint(Mouse.CursorPos).WorkareaRect;
SetBounds(R.Left + 50, R.Top + 50, R.Width - 100, R.Height - 100);
end;
procedure TfrmMain.SetMnemonicFilter(const Filter: String; ExactMatch: Boolean;
DiffingMode: Boolean);
procedure ApplyMnemonicFilter(Filter: TInstructionFilter; out IsVisible: Boolean;
const FilterText: String; FilterLength: Integer);
var
D: TInstructionDefinition;
C: TDefinitionContainer;
I: Integer;
B: Boolean;
NodeData: PEditorNodeData;
begin
IsVisible := (FilterLength = 0) and (not DiffingMode);
if (iffIsDefinitionContainer in Filter.FilterFlags) then
begin
C := (Filter as TDefinitionContainer);
for I := 0 to C.DefinitionCount - 1 do
begin
B := IsVisible;
D := C.Definitions[I];
if (not IsVisible) then
begin
if (Length(D.Mnemonic) >= FilterLength) then
begin
if (ExactMatch) then
begin
B := (CompareStr(FilterText, LowerCase(D.Mnemonic)) = 0);
end else
begin
B := (CompareStr(FilterText, AnsiLowerCase(Copy(D.Mnemonic, 1, FilterLength))) = 0);
end;
end;
end;
if (DiffingMode) then
begin
NodeData := EditorTree.GetNodeData(GetTreeNode(D));
B := B and (NodeData^.DiffingState <> dsDefault);
end;
EditorTree.IsVisible[GetTreeNode(D)] := B;
IsVisible := IsVisible or B;
end;
end else
begin
for I := 0 to Filter.Capacity - 1 do
begin
if (not Assigned(Filter.Items[I])) then Continue;
ApplyMnemonicFilter(Filter.Items[I], B, FilterText, FilterLength);
EditorTree.IsVisible[GetTreeNode(Filter.Items[I])] := B;
IsVisible := IsVisible or B;
end;
EditorTree.IsVisible[GetTreeNode(Filter)] := IsVisible;
end;
end;
var
FilterText: String;
FilterLength: Integer;
IsVisible: Boolean;
begin
EditorTree.BeginUpdate;
try
FilterText := AnsiLowerCase(Filter);
FilterLength := Length(Filter);
ApplyMnemonicFilter(FEditor.RootTable, IsVisible, FilterText, FilterLength);
finally
EditorTree.EndUpdate;
end;
end;
procedure TfrmMain.UpdateControls;
var
NodeData: PEditorNodeData;
begin
lbLoadDatabase.Enabled := (not lbDiffingMode.Down);
lbSaveDatabase.Enabled := FHasUnsavedChanges and (not lbDiffingMode.Down);
NodeData := EditorTree.GetNodeData(EditorTree.FocusedNode);
bbDuplicateDefinition.Enabled :=
Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition);
bbDeleteDefinition.Enabled :=
Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition);
bbClipboardCopy.Enabled := Assigned(NodeData);
bbClipboardCut.Enabled :=
Assigned(NodeData) and (NodeData^.NodeType = ntInstructionDefinition);
bbExpandLeaf.Enabled :=
Assigned(NodeData) and (NodeData^.NodeType <> ntInstructionDefinition);
bbCollapseLeaf.Enabled :=
Assigned(NodeData) and (NodeData^.NodeType <> ntInstructionDefinition);
end;
procedure TfrmMain.UpdateExpandedProperties;
var
I: Integer;
begin
if (Assigned(Inspector.InspectedObject)) then
begin
if (Inspector.InspectedObject is TInstructionFilter) then
begin
FExpandedFilterProperties.Clear;
end;
if (Inspector.InspectedObject is TInstructionDefinition) then
begin
FExpandedDefinitionProperties.Clear;
end;
for I := 0 to Inspector.Rows.Count - 1 do
begin
if (Inspector.Rows[I].Expanded) then
begin
if (Inspector.InspectedObject is TInstructionFilter) then
begin
FExpandedFilterProperties.Add(
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName);
end;
if (Inspector.InspectedObject is TInstructionDefinition) then
begin
FExpandedDefinitionProperties.Add(
(Inspector.Rows[I] as TcxPropertyRow).PropertyEditor.GetName);
end;
end;
end;
end;
end;
procedure TfrmMain.UpdateStatistic;
var
Mnemonics: TDictionary<String, Boolean>;
Node: PVirtualNode;
NodeData: PEditorNodeData;
begin
if (not FUpdating) then
begin
Mnemonics := TDictionary<String, Boolean>.Create;
try
Node := EditorTree.GetFirst;
while Assigned(Node) do
begin
NodeData := EditorTree.GetNodeData(Node);
if (NodeData^.NodeType = ntInstructionDefinition) then
begin
if (not Mnemonics.ContainsKey(NodeData^.Definition.Mnemonic)) then
begin
Mnemonics.Add(NodeData^.Definition.Mnemonic, true);
end;
end;
Node := EditorTree.GetNext(Node);
end;
StatusBar.Panels[2].Text := 'Mnemonics: ' + IntToStr(Mnemonics.Count);
finally
Mnemonics.Free;
end;
StatusBar.Panels[3].Text := 'Definitions: ' + IntToStr(FEditor.DefinitionCount);
StatusBar.Panels[4].Text := 'Filters: ' + IntToStr(FEditor.FilterCount);
end;
end;
procedure TfrmMain.InspectorItemChanged(Sender: TObject; AOldRow: TcxCustomRow;
AOldCellIndex: Integer);
var
Row: TcxPropertyRow;
S: String;
begin
lblPropertyInfo.Caption := 'No info text available';
Row := (Inspector.FocusedRow as TcxPropertyRow);
if Assigned(Row) and Assigned(Row.PropertyEditor) then
begin
S := Row.PropertyEditor.GetName;
while (Assigned(Row.Parent)) do
begin
Row := (Row.Parent as TcxPropertyRow);
S := Row.PropertyEditor.GetName + '.' + S;
end;
if (Inspector.InspectedObject is TInstructionFilter) then
begin
S := 'Filter.' + S;
end else if (Inspector.InspectedObject is TInstructionDefinition) then
begin
S := 'Definition.' + S;
end;
lblPropertyInfo.Caption := GetPropertyHint(S);
end;
end;
end.