unit spkte_EditWindow;

{$MODE Delphi}

interface

uses
  LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, {DesignIntf, DesignEditors,} StdCtrls, ImgList, ComCtrls,
  ActnList, Menus, ComponentEditors, PropEdits,
  SpkToolbar, spkt_Tab, spkt_Pane, spkt_BaseItem, spkt_Buttons, spkt_Types, spkt_Checkboxes;

type TCreateItemFunc = function(Pane : TSpkPane) : TSpkBaseItem;

type

  { TfrmEditWindow }

  TfrmEditWindow = class(TForm)
    aAddCheckbox: TAction;
    aAddRadioButton: TAction;
    ilTreeImages_150: TImageList;
    ilActionImages_150: TImageList;
    ilActionImages_200: TImageList;
    ilTreeImages_200: TImageList;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    tvStructure: TTreeView;
    ilTreeImages: TImageList;
    tbToolBar: TToolBar;
    tbAddTab: TToolButton;
    ilActionImages: TImageList;
    tbRemoveTab: TToolButton;
    ToolButton3: TToolButton;
    tbAddPane: TToolButton;
    tbRemovePane: TToolButton;
    ActionList: TActionList;
    aAddTab: TAction;
    aRemoveTab: TAction;
    aAddPane: TAction;
    aRemovePane: TAction;
    ToolButton6: TToolButton;
    aMoveUp: TAction;
    aMoveDown: TAction;
    tbMoveUp: TToolButton;
    tbMoveDown: TToolButton;
    ToolButton9: TToolButton;
    tbAddItem: TToolButton;
    tbRemoveItem: TToolButton;
    pmAddItem: TPopupMenu;
    SpkLargeButton1: TMenuItem;
    aAddLargeButton: TAction;
    aRemoveItem: TAction;
    aAddSmallButton: TAction;
    SpkSmallButton1: TMenuItem;
    pmStructure: TPopupMenu;
    Addtab1: TMenuItem;
    Removetab1: TMenuItem;
    N1: TMenuItem;
    Addpane1: TMenuItem;
    Removepane1: TMenuItem;
    N2: TMenuItem;
    Additem1: TMenuItem;
    SpkLargeButton2: TMenuItem;
    SpkSmallButton2: TMenuItem;
    Removeitem1: TMenuItem;
    N3: TMenuItem;
    Moveup1: TMenuItem;
    Movedown1: TMenuItem;
    procedure tvStructureChange(Sender: TObject; Node: TTreeNode);
    procedure aAddTabExecute(Sender: TObject);
    procedure aRemoveTabExecute(Sender: TObject);
    procedure aAddPaneExecute(Sender: TObject);
    procedure aRemovePaneExecute(Sender: TObject);
    procedure aMoveUpExecute(Sender: TObject);
    procedure aMoveDownExecute(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure aAddLargeButtonExecute(Sender: TObject);
    procedure aRemoveItemExecute(Sender: TObject);
    procedure aAddSmallButtonExecute(Sender: TObject);
    procedure aAddCheckboxExecute(Sender: TObject);
    procedure aAddRadioButtonExecute(Sender: TObject);
    procedure tvStructureDeletion(Sender:TObject; Node:TTreeNode);
    procedure tvStructureEdited(Sender: TObject; Node: TTreeNode; var S: string);
    procedure tvStructureKeyDown(Sender: TObject; var Key: Word;
      {%H-}Shift: TShiftState);
    procedure FormActivate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  protected
    FToolbar: TSpkToolbar;
    FDesigner: TComponentEditorDesigner;

    procedure CheckActionsAvailability;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;

    procedure AddItem(ItemClass: TSpkBaseItemClass);
    function GetItemCaption(Item: TSpkBaseItem): string;
    procedure SetItemCaption(Item: TSpkBaseItem; const Value: String);

    procedure DoRemoveTab;
    procedure DoRemovePane;
    procedure DoRemoveItem;

    function CheckValidTabNode(Node: TTreeNode): boolean;
    function CheckValidPaneNode(Node: TTreeNode): boolean;
    function CheckValidItemNode(Node: TTreeNode): boolean;

    procedure UpdatePPI;
  public
    { Public declarations }
    function ValidateTreeData: boolean;
    procedure BuildTreeData;
    procedure RefreshNames;
    procedure SetData(AToolbar: TSpkToolbar; ADesigner: TComponentEditorDesigner);

    property Toolbar: TSpkToolbar read FToolbar;
  end;

var
  frmEditWindow: TfrmEditWindow;

implementation

{$R *.lfm}

resourcestring
  RSCannotMoveAboveFirstElement = 'You can not move above the top of the first element!';
  RSCannotMoveBeyondLastElement = 'You can not move beyond the last element!';
  RSDamagedTreeStructure = 'Damaged tree structure!';
  RSIncorrectFieldData = 'Incorrect data in the field!';
  RSIncorrectObjectInTree = 'Incorrect object attached to the tree!';
  RSNoObjectSelected = 'No object selected!';
  RSNoObjectSelectedToMove = 'No object selected to move!';


{ TfrmEditWindow }

procedure TfrmEditWindow.aAddPaneExecute(Sender: TObject);
var
  Obj: TObject;
  Node: TTreeNode;
  NewNode: TTreeNode;
  Tab: TSpkTab;
  Pane: TSpkPane;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  if FDesigner.PropertyEditorHook = nil then
    exit;

  Node := tvStructure.Selected;
  if Node = nil then
    raise Exception.Create('TfrmEditWindow.aAddPaneExecute: ' + RSNoObjectSelected);
  if Node.Data = nil then
    raise Exception.Create('TfrmEditWindow.aAddPaneExecute: ' + RSDamagedTreeStructure);

  Obj := TObject(Node.Data);
  if Obj is TSpkTab then
  begin
    Tab := TSpkTab(Obj);
    Pane := TSpkPane.Create(FToolbar.Owner);
    Pane.Parent := FToolbar;
    Pane.Name := FDesigner.CreateUniqueComponentName(Pane.ClassName);
    Tab.Panes.AddItem(Pane);
    NewNode := tvStructure.Items.AddChild(Node, Pane.Caption);
    NewNode.Data := Pane;
    NewNode.ImageIndex := 1;
    NewNode.SelectedIndex := 1;
    NewNode.Selected := true;
    CheckActionsAvailability;
  end else
  if Obj is TSpkPane then
  begin
    if not(CheckValidPaneNode(Node)) then
      raise Exception.Create('TfrmEditWindow.aAddPaneExecute: ' + RSDamagedTreeStructure);
    Tab := TSpkTab(Node.Parent.Data);
    Pane := TSpkPane.Create(FToolbar.Owner);
    Pane.Parent := FToolbar;
    Pane.Name := FDesigner.CreateUniqueComponentName(Pane.ClassName);
    Tab.Panes.AddItem(Pane);
    NewNode := tvStructure.Items.AddChild(Node.Parent, Pane.Caption);
    NewNode.Data := Pane;
    NewNode.ImageIndex := 1;
    NewNode.SelectedIndex := 1;
    NewNode.Selected := true;
    CheckActionsAvailability;
  end else
  if Obj is TSpkBaseItem then
  begin
    if not(CheckValidItemNode(Node)) then
      raise Exception.Create('TfrmEditWindow.aAddPaneExecute: ' + RSDamagedTreeStructure);
    Tab := TSpkTab(Node.Parent.Parent.Data);
    Pane := TSpkPane.Create(FToolbar.Owner);
    Pane.Parent := FToolbar;
    Pane.Name := FDesigner.CreateUniqueComponentName(Pane.ClassName);
    Tab.Panes.AddItem(Pane);
    NewNode := tvStructure.Items.AddChild(Node.Parent.Parent, Pane.Caption);
    NewNode.Data := Pane;
    NewNode.ImageIndex := 1;
    NewNode.SelectedIndex := 1;
    NewNode.Selected := true;
    CheckActionsAvailability;
  end else
    raise Exception.Create('TfrmEditWindow.aAddPaneExecute: ' + RSIncorrectObjectInTree);

  FDesigner.PropertyEditorHook.PersistentAdded(Pane,True);
  FDesigner.Modified;
end;

procedure TfrmEditWindow.aAddSmallButtonExecute(Sender: TObject);
begin
  AddItem(TSpkSmallButton);
end;

procedure TfrmEditWindow.aAddLargeButtonExecute(Sender: TObject);
begin
  AddItem(TSpkLargeButton);
end;

procedure TfrmEditWindow.aAddCheckboxExecute(Sender: TObject);
begin
  AddItem(TSpkCheckbox);
end;

procedure TfrmEditWindow.aAddRadioButtonExecute(Sender: TObject);
begin
  AddItem(TSpkRadioButton);
end;

procedure TfrmEditWindow.aAddTabExecute(Sender: TObject);
var
  Node: TTreeNode;
  Tab: TSpkTab;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  if FDesigner.PropertyEditorHook = nil then
    exit;

  Tab := TSpkTab.Create(FToolbar.Owner);
  Tab.Parent := FToolbar;
  FToolbar.Tabs.AddItem(Tab);
  Tab.Name := FDesigner.CreateUniqueComponentName(Tab.ClassName);
  Node := tvStructure.Items.AddChild(nil, Tab.Caption);
  Node.Data := Tab;
  Node.ImageIndex := 0;
  Node.SelectedIndex := 0;
  Node.Selected := true;
  CheckActionsAvailability;

  FDesigner.PropertyEditorHook.PersistentAdded(Tab,True);
  FDesigner.Modified;
end;

procedure TfrmEditWindow.AddItem(ItemClass: TSpkBaseItemClass);
var
  Node: TTreeNode;
  Obj: TObject;
  Pane: TSpkPane;
  Item: TSpkBaseItem;
  NewNode: TTreeNode;
  s: string;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
   exit;
  if FDesigner.PropertyEditorHook = nil then
   exit;

  Node := tvStructure.Selected;
  if Node = nil then
    raise Exception.Create('TfrmEditWindow.AddItem: ' + RSNoObjectSelected);
  if Node.Data = nil then
    raise Exception.Create('TfrmEditWindow.AddItem: ' + RSDamagedTreeStructure);

  Obj := TObject(Node.Data);
  if Obj is TSpkPane then
  begin
    Pane := TSpkPane(Obj);
    Item := ItemClass.Create(FToolbar.Owner);
    Item.Parent := FToolbar;
    Pane.Items.AddItem(Item);
    Item.Name := FDesigner.CreateUniqueComponentName(Item.ClassName);
    s := GetItemCaption(Item);
    NewNode := tvStructure.Items.AddChild(Node, s);
    NewNode.Data := Item;
    NewNode.Selected := true;
    CheckActionsAvailability;
  end else
  if Obj is TSpkBaseItem then
  begin
    if not CheckValidItemNode(Node) then
      raise Exception.Create('TfrmEditWindow.AddItem: ' + RSDamagedTreeStructure);
    Pane := TSpkPane(Node.Parent.Data);
    Item := ItemClass.Create(FToolbar.Owner);
    Item.Parent := FToolbar;
    Pane.Items.AddItem(Item);
    Item.Name := FDesigner.CreateUniqueComponentName(Item.ClassName);
    s := GetItemCaption(Item);
    NewNode := tvStructure.Items.AddChild(Node.Parent, s);
    NewNode.Data := Item;
    NewNode.Selected := true;
    CheckActionsAvailability;
  end else
    raise Exception.Create('TfrmEditWindow.AddItem: ' + RSIncorrectObjectInTree);
  if ItemClass = TSpkLargeButton then
    NewNode.ImageIndex := 2
  else
  if ItemClass = TSpkSmallButton then
    NewNode.ImageIndex := 3
  else
  if ItemClass = TSpkCheckbox then
    NewNode.ImageIndex := 4
  else
  if ItemClass = TSpkRadioButton then
    NewNode.ImageIndex := 5
  else
    raise Exception.Create('Item class not supported');
  NewNode.SelectedIndex := NewNode.ImageIndex;
  FDesigner.PropertyEditorHook.PersistentAdded(Item,True);
  FDesigner.Modified;
end;

procedure TfrmEditWindow.aMoveDownExecute(Sender: TObject);
var
  Node: TTreeNode;
  Tab: TSpkTab;
  Pane: TSpkPane;
  Obj: TObject;
  index: Integer;
  Item: TSpkBaseItem;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  Node := tvStructure.Selected;
  if Node = nil then
    raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSNoObjectSelectedToMove);
  if Node.Data = nil then
    raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);

  Obj := TObject(Node.Data);
  if Obj is TSpkTab then
  begin
    if not CheckValidTabNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);

    Tab := TSpkTab(Node.Data);
    index := FToolbar.Tabs.IndexOf(Tab);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);
    if (index = FToolbar.Tabs.Count - 1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSCannotMoveBeyondLastElement);

    FToolbar.Tabs.Exchange(index, index+1);
    FToolbar.TabIndex := index+1;

    Node.GetNextSibling.MoveTo(Node, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end
  else
  if Obj is TSpkPane then
  begin
    if not CheckValidPaneNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);

    Pane := TSpkPane(Node.Data);
    Tab := TSpkTab(Node.Parent.Data);

    index := Tab.Panes.IndexOf(Pane);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);
    if (index = Tab.Panes.Count - 1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSCannotMoveBeyondLastElement);

    Tab.Panes.Exchange(index, index+1);

    Node.GetNextSibling.MoveTo(Node, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end
  else
  if Obj is TSpkBaseItem then
  begin
    if not CheckValidItemNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveDown.Execute: ' + RSDamagedTreeStructure);

    Item := TSpkBaseItem(Node.Data);
    Pane := TSpkPane(Node.Parent.Data);

    index := Pane.Items.IndexOf(Item);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSDamagedTreeStructure);
    if (index = Pane.Items.Count - 1) then
      raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSCannotMoveBeyondLastElement);

    Pane.Items.Exchange(index, index+1);

    Node.GetNextSibling.MoveTo(Node, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end
  else
    raise Exception.Create('TfrmEditWindow.aMoveDownExecute: ' + RSIncorrectObjectInTree);
end;

procedure TfrmEditWindow.aMoveUpExecute(Sender: TObject);
var
  Node: TTreeNode;
  Tab: TSpkTab;
  Pane: TSpkPane;
  Obj: TObject;
  index: Integer;
  Item: TSpkBaseItem;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  Node := tvStructure.Selected;
  if Node = nil then
    raise Exception.Create('TfrmEditWindow.aMoveUpExecute: '+ RSNoObjectSelectedToMove);
  if Node.Data = nil then
    raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);

  Obj := TObject(Node.Data);
  if Obj is TSpkTab then
  begin
    if not CheckValidTabNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);

    Tab := TSpkTab(Node.Data);
    index := FToolbar.Tabs.IndexOf(Tab);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);
    if (index = 0) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSCannotMoveAboveFirstElement);

    FToolbar.Tabs.Exchange(index, index-1);
    FToolbar.TabIndex := index-1;

    Node.MoveTo(Node.getPrevSibling, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end
  else
  if Obj is TSpkPane then
  begin
    if not CheckValidPaneNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);

    Pane := TSpkPane(Node.Data);
    Tab := TSpkTab(Node.Parent.Data);
    index := Tab.Panes.IndexOf(Pane);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);
    if (index = 0) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSCannotMoveAboveFirstElement);

    Tab.Panes.Exchange(index, index-1);

    Node.MoveTo(Node.GetPrevSibling, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end
  else
  if Obj is TSpkBaseItem then
  begin
    if not CheckValidItemNode(Node) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);

    Item := TSpkBaseItem(Node.Data);
    Pane := TSpkPane(Node.Parent.Data);
    index := Pane.Items.IndexOf(Item);
    if (index = -1) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSDamagedTreeStructure);
    if (index = 0) then
      raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSCannotMoveAboveFirstElement);

    Pane.Items.Exchange(index, index-1);

    Node.MoveTo(Node.GetPrevSibling, naInsert);
    Node.Selected := true;
    CheckActionsAvailability;
  end else
    raise Exception.Create('TfrmEditWindow.aMoveUpExecute: ' + RSIncorrectObjectInTree);
end;

procedure TfrmEditWindow.aRemoveItemExecute(Sender: TObject);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  DoRemoveItem;
end;

procedure TfrmEditWindow.aRemovePaneExecute(Sender: TObject);

begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  DoRemovePane;
end;

procedure TfrmEditWindow.aRemoveTabExecute(Sender: TObject);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  DoRemoveTab;
end;

procedure TfrmEditWindow.CheckActionsAvailability;
var
  Node: TTreeNode;
  Obj: TObject;
  Tab: TSpkTab;
  Pane: TSpkPane;
  index: integer;
  Item: TSpkBaseItem;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
  begin
    // Brak toolbara lub designera
    aAddTab.Enabled := false;
    aRemoveTab.Enabled := false;
    aAddPane.Enabled := false;
    aRemovePane.Enabled := false;
    aAddLargeButton.Enabled := false;
    aAddSmallButton.Enabled := false;
    aAddCheckbox.Enabled := false;
    aAddRadioButton.Enabled := false;
    aRemoveItem.Enabled := false;
    aMoveUp.Enabled := false;
    aMoveDown.Enabled := false;
  end
  else
  begin
    Node := tvStructure.Selected;
    if Node = nil then
    begin
      // Pusty toolbar
      aAddTab.Enabled := true;
      aRemoveTab.Enabled := false;
      aAddPane.Enabled := false;
      aRemovePane.Enabled := false;
      aAddLargeButton.Enabled := false;
      aAddSmallButton.Enabled := false;
      aAddCheckbox.Enabled := false;
      aAddRadioButton.Enabled := false;
      aRemoveItem.Enabled := false;
      aMoveUp.Enabled := false;
      aMoveDown.Enabled := false;
    end
    else
    begin
      Obj := TObject(Node.Data);
      if Obj = nil then
        raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSIncorrectFieldData);

      if Obj is TSpkTab then
      begin
        Tab := Obj as TSpkTab;

        if not CheckValidTabNode(Node) then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        aAddTab.Enabled := true;
        aRemoveTab.Enabled := true;
        aAddPane.Enabled := true;
        aRemovePane.Enabled := false;
        aAddLargeButton.Enabled := false;
        aAddSmallButton.Enabled := false;
        aAddCheckbox.Enabled := false;
        aAddRadioButton.Enabled := false;
        aRemoveItem.Enabled := false;

        index := FToolbar.Tabs.IndexOf(Tab);
        if index = -1 then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        aMoveUp.Enabled := (index > 0);
        aMoveDown.enabled := (index < FToolbar.Tabs.Count-1);
      end else
      if Obj is TSpkPane then
      begin
        Pane := TSpkPane(Obj);
        if not(CheckValidPaneNode(Node)) then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        Tab := TSpkTab(Node.Parent.Data);

        aAddTab.Enabled := true;
        aRemoveTab.Enabled := false;
        aAddPane.Enabled := true;
        aRemovePane.Enabled := true;
        aAddLargeButton.Enabled := true;
        aAddSmallButton.Enabled := true;
        aAddCheckbox.Enabled := true;
        aAddRadiobutton.Enabled := true;
        aRemoveItem.Enabled := false;

        index := Tab.Panes.IndexOf(Pane);
        if index = -1 then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        aMoveUp.Enabled := (index > 0);
        aMoveDown.Enabled := (index < Tab.Panes.Count-1);
      end else
      if Obj is TSpkBaseItem then
      begin
        Item := TSpkBaseItem(Obj);
        if not CheckValidItemNode(Node) then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        Pane := TSpkPane(Node.Parent.Data);

        aAddTab.Enabled := true;
        aRemoveTab.Enabled := false;
        aAddPane.Enabled := true;
        aRemovePane.Enabled := false;
        aAddLargeButton.Enabled := true;
        aAddSmallButton.Enabled := true;
        aAddCheckbox.Enabled := true;
        aAddRadioButton.Enabled := true;
        aRemoveItem.Enabled := true;

        index := Pane.Items.IndexOf(Item);
        if index = -1 then
          raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSDamagedTreeStructure);

        aMoveUp.Enabled := (index > 0);
        aMoveDown.Enabled := (index < Pane.Items.Count - 1);
      end else
        raise Exception.Create('TfrmEditWindow.CheckActionsAvailability: ' + RSIncorrectObjectInTree);
    end;
  end;
end;

function TfrmEditWindow.CheckValidItemNode(Node: TTreeNode): boolean;
begin
  Result := false;
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  {$B-}
  Result:=(Node <> nil) and
          (Node.Data <> nil) and
          (TObject(Node.Data) is TSpkBaseItem) and
          CheckValidPaneNode(Node.Parent);
end;

function TfrmEditWindow.CheckValidPaneNode(Node: TTreeNode): boolean;
begin
  Result := false;
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  {$B-}
  Result := (Node <> nil) and
            (Node.Data <> nil) and
            (TObject(Node.Data) is TSpkPane) and
            CheckValidTabNode(Node.Parent);
end;

function TfrmEditWindow.CheckValidTabNode(Node: TTreeNode): boolean;
begin
  Result := false;
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  {$B-}
  result := (Node <> nil) and
            (Node.Data <> nil) and
            (TObject(Node.Data) is TSpkTab);
end;

procedure TfrmEditWindow.FormActivate(Sender: TObject);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  UpdatePPI;
  if not ValidateTreeData then
    BuildTreeData;
end;

procedure TfrmEditWindow.FormDestroy(Sender: TObject);
begin
  if FToolbar <> nil then
    FToolbar.RemoveFreeNotification(self);
end;

procedure TfrmEditWindow.FormShow(Sender: TObject);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  BuildTreeData;
end;

function TfrmEditWindow.GetItemCaption(Item: TSpkBaseItem): string;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;
  if Item is TSpkBaseButton then
    Result := TSpkBaseButton(Item).Caption
  else
    Result := '<Unknown caption>';
end;

procedure TfrmEditWindow.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;

  if (AComponent = FToolbar) and (Operation = opRemove) then
  begin
    // The toolbar is currently released, whose content is displayed in the
    // editor window. Need to clean up the content - otherwise the window will
    // have references to the already removed toolbars, which will end in AVs ...
    SetData(nil, nil);
  end;
end;

procedure TfrmEditWindow.SetItemCaption(Item: TSpkBaseItem; const Value : string);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  if Item is TSpkBaseButton then
    TSpkBaseButton(Item).Caption := Value;
end;

procedure TfrmEditWindow.SetData(AToolbar: TSpkToolbar; ADesigner: TComponentEditorDesigner);
begin
  if FToolbar <> nil then
    FToolbar.RemoveFreeNotification(self);

  FToolbar := AToolbar;
  FDesigner := ADesigner;

  if FToolbar <> nil then
    FToolbar.FreeNotification(self);

  BuildTreeData;
end;

procedure TfrmEditWindow.DoRemoveItem;
var
  Item: TSpkBaseItem;
  index: Integer;
  Node: TTreeNode;
  Pane: TSpkPane;
  NextNode: TTreeNode;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  Node := tvStructure.Selected;
  if not (CheckValidItemNode(Node)) then
    raise Exception.Create('TfrmEditWindow.aRemoveItemExecute: ' + RSDamagedTreeStructure);
  Item := TSpkBaseItem(Node.Data);
  Pane := TSpkPane(Node.Parent.Data);
  index := Pane.Items.IndexOf(Item);
  if index = -1 then
    raise Exception.Create('TfrmEditWindow.aRemoveItemExecute: ' + RSDamagedTreeStructure);
  if Node.getPrevSibling <> nil then
    NextNode := Node.getPrevSibling
  else if Node.GetNextSibling <> nil then
    NextNode := Node.getNextSibling
  else
    NextNode := Node.Parent;
  Pane.Items.Delete(index);
  tvStructure.Items.delete(node);
  NextNode.Selected := true;
  CheckActionsAvailability;
end;

procedure TfrmEditWindow.DoRemovePane;
var
  Pane: TSpkPane;
  NextNode: TTreeNode;
  index: Integer;
  Node: TTreeNode;
  Tab: TSpkTab;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  Node := tvStructure.Selected;
  if not (CheckValidPaneNode(Node)) then
    raise Exception.Create('TfrmEditWindow.aRemovePaneExecute: ' + RSDamagedTreeStructure);
  Pane := TSpkPane(Node.Data);
  Tab := TSpkTab(Node.Parent.Data);
  index := Tab.Panes.IndexOf(Pane);
  if index = -1 then
    raise Exception.Create('TfrmEditWindow.aRemovePaneExecute: ' + RSDamagedTreeStructure);
  if Node.GetPrevSibling <> nil then
    NextNode := Node.GetPrevSibling
  else if Node.GetNextSibling <> nil then
    NextNode := Node.GetNextSibling
  else
    NextNode := Node.Parent;
  Tab.Panes.Delete(index);
  tvStructure.Items.Delete(Node);
  NextNode.Selected := true;
  CheckActionsAvailability;
end;

procedure TfrmEditWindow.DoRemoveTab;
var
  Node: TTreeNode;
  Tab: TSpkTab;
  index: Integer;
  NextNode: TTreeNode;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  Node := tvStructure.Selected;
  if not (CheckValidTabNode(Node)) then
    raise Exception.Create('TfrmEditWindow.aRemoveTabExecute: ' + RSDamagedTreeStructure);
  Tab := TSpkTab(Node.Data);
  index := FToolbar.Tabs.IndexOf(Tab);
  if index = -1 then
    raise Exception.Create('TfrmEditWindow.aRemoveTabExecute: ' + RSDamagedTreeStructure);
  if Node.GetPrevSibling <> nil then
    NextNode := Node.GetPrevSibling
  else if Node.GetNextSibling <> nil then
    NextNode := Node.GetNextSibling
  else
    NextNode := nil;
  FToolbar.Tabs.Delete(index);
  tvStructure.Items.Delete(Node);
  if Assigned(NextNode) then
  begin
    // The OnChange event will trigger an update of the selected object in
    // the Object Inspector
    NextNode.Selected := true;
    CheckActionsAvailability;
  end
  else
  begin
    // There are no more objects in the list, but something has to be displayed
    // in the Object Inspector - so we will display the toolbars itself
    // (otherwise the IDE will attempt to display the object's properties in
    // the Object Inspector, which will end, say, not very nice)
    //DesignObj := PersistentToDesignObject(FToolbar);
    FDesigner.SelectOnlyThisComponent(FToolbar);
    CheckActionsAvailability;
  end;
end;

procedure TfrmEditWindow.BuildTreeData;
var
  i: Integer;
  panenode: TTreeNode;
  j: Integer;
  tabnode: TTreeNode;
  k : Integer;
  itemnode: TTreeNode;
  Obj: TSpkBaseItem;
  s: string;
  node: TTreeNode;
begin
  Caption:='Editing TSpkToolbar contents';

  // Clear tree, but don't remove existing toolbar children from the form
  tvStructure.OnDeletion := nil;
  tvStructure.Items.Clear;
  tvStructure.OnDeletion := tvStructureDeletion;

  if (FToolbar <> nil) and (FDesigner <> nil) then
  begin
    for i := 0 to FToolbar.Tabs.Count - 1 do
    begin
      tabnode := tvStructure.Items.AddChild(nil, FToolbar.Tabs[i].Caption);
      tabnode.ImageIndex := 0;
      tabnode.SelectedIndex := 0;
      tabnode.Data := FToolbar.Tabs[i];
      for j := 0 to FToolbar.Tabs.Items[i].Panes.Count - 1 do
      begin
        panenode := tvStructure.Items.AddChild(tabnode, FToolbar.Tabs[i].Panes[j].Caption);
        panenode.ImageIndex := 1;
        panenode.SelectedIndex := 1;
        panenode.Data := FToolbar.Tabs[i].Panes[j];
        for k := 0 to FToolbar.Tabs[i].Panes[j].Items.Count - 1 do
        begin
          Obj := FToolbar.Tabs[i].Panes[j].Items[k];
          s := GetItemCaption(Obj);
          itemnode := tvStructure.Items.AddChild(panenode,s);
          itemnode.Imageindex := 2;
          itemnode.Selectedindex := 2;
          itemnode.Data := Obj;
        end;
      end;
    end;
  end;

  if (tvStructure.Items.Count > 0) and (FToolbar.TabIndex > -1) then begin
    node := tvStructure.Items[0];
    while (node <> nil) do begin
      if TSpkTab(node.Data) = FToolbar.Tabs[FToolbar.TabIndex] then break;
      node := node.GetNextSibling;
    end;
    if (node <> nil) then begin
      node.Selected := true;
      node.Expand(true);
    end;
  end;

  CheckActionsAvailability;
end;

procedure TfrmEditWindow.RefreshNames;
var
  tabnode, panenode, itemnode: TTreeNode;
  Obj: TSpkBaseItem;
  s: string;

begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  tabnode := tvStructure.Items.GetFirstNode;
  while tabnode<>nil do
  begin
    if not CheckValidTabNode(tabnode) then
      raise Exception.Create('TfrmEditWindow.RefreshNames: ' + RSDamagedTreeStructure);

    tabnode.Text := TSpkTab(tabnode.Data).Caption;

    panenode := tabnode.getFirstChild;
    while panenode <> nil do
    begin
      if not CheckValidPaneNode(panenode) then
        raise Exception.Create('TfrmEditWindow.RefreshNames: ' + RSDamagedTreeStructure);

      panenode.Text := TSpkPane(panenode.Data).Caption;

      itemnode := panenode.getFirstChild;
      while itemnode <> nil do
      begin
        if not CheckValidItemNode(itemnode) then
          raise Exception.Create('TfrmEditWindow.RefreshNames: ' + RSDamagedTreeStructure);

        Obj := TSpkBaseItem(itemnode.Data);
        s := GetItemCaption(Obj);
        itemnode.Text := s;
        itemnode := itemnode.getNextSibling;
      end;

      panenode := panenode.getNextSibling;
    end;

    tabnode := tabnode.getNextSibling;
  end;
end;

procedure TfrmEditWindow.tvStructureChange(Sender: TObject; Node: TTreeNode);
var
  Obj: TObject;
  Tab: TSpkTab;
  Pane: TSpkPane;
  Item: TSpkBaseItem;
  index: integer;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  if Assigned(Node) then
  begin
    Obj := TObject(Node.Data);
    if Obj = nil then
      raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSIncorrectFieldData);
    if Obj is TSpkTab then
    begin
      Tab := TSpkTab(Obj);
      FDesigner.SelectOnlyThisComponent(Tab);
      index := FToolbar.Tabs.IndexOf(Tab);
      if index=-1 then
        raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSDamagedTreeStructure);
      FToolbar.TabIndex := index;
    end else
    if Obj is TSpkPane then
    begin
      Pane := TSpkPane(Obj);
      FDesigner.SelectOnlyThisComponent(Pane);
      if not(CheckValidPaneNode(Node)) then
        raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSDamagedTreeStructure);
      Tab := TSpkTab(Node.Parent.Data);
      index := FToolbar.Tabs.IndexOf(Tab);
      if index = -1 then
        raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSDamagedTreeStructure);
      FToolbar.TabIndex := index;
    end else
    if Obj is TSpkBaseItem then
    begin
      Item := TSpkBaseItem(Obj);
      FDesigner.SelectOnlyThisComponent(Item);
      if not CheckValidItemNode(Node) then
        raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSDamagedTreeStructure);
      Tab := TSpkTab(Node.Parent.Parent.Data);
      index := FToolbar.Tabs.IndexOf(Tab);
      if index = -1 then
        raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSDamagedTreeStructure);
      FToolbar.TabIndex := index;
    end else
      raise Exception.Create('TfrmEditWindow.tvStructureChange: ' + RSIncorrectObjectInTree);
   end else
     FDesigner.SelectOnlyThisComponent(FToolbar);

  CheckActionsAvailability;
end;

procedure TfrmEditWindow.tvStructureDeletion(Sender:TObject; Node:TTreeNode);
var
  RunNode: TTreeNode;
begin
  if Node = nil then
    exit;
  // Recursively delete children and destroy their data
  RunNode := Node.GetFirstChild;
  while RunNode <> nil do begin
    RunNode.Delete;
    RunNode := RunNode.GetNextSibling;
  end;
  // Destroy node's data
  TSpkComponent(Node.Data).Free;
end;

procedure TfrmEditWindow.tvStructureEdited(Sender: TObject; Node: TTreeNode;
  var S: string);
var
  Tab: TSpkTab;
  Pane: TSpkPane;
  Item: TSpkBaseItem;
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  if Node.Data = nil then
    raise Exception.Create('TfrmEditWindow.tvStructureEdited: ' + RSDamagedTreeStructure);

  if TObject(Node.Data) is TSpkTab then
  begin
    Tab := TSpkTab(Node.Data);
    Tab.Caption := S;
    FDesigner.Modified;
  end else
  if TObject(Node.Data) is TSpkPane then
  begin
    Pane := TSpkPane(Node.Data);
    Pane.Caption := S;
    FDesigner.Modified;
  end else
  if TObject(Node.Data) is TSpkBaseItem then
  begin
    Item := TSpkBaseItem(Node.Data);
    SetItemCaption(Item, S);
    FDesigner.Modified;
  end else
    raise Exception.Create('TfrmEditWindow.tvStructureEdited: ' + RSDamagedTreeStructure);
end;

procedure TfrmEditWindow.tvStructureKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  if Key = VK_DELETE then
  begin
    if tvStructure.Selected <> nil then
    begin
      // We check what kind of object is selected - just test the type of the object
      if TObject(tvStructure.Selected.Data) is TSpkTab then
        DoRemoveTab
      else if TObject(tvStructure.Selected.Data) is TSpkPane then
        DoRemovePane
      else if TObject(tvStructure.Selected.Data) is TSpkBaseItem then
        DoRemoveItem
      else
        raise Exception.Create('TfrmEditWindow.tvStructureKeyDown: ' + RSDamagedTreeStructure);
    end;
   end;
end;

function TfrmEditWindow.ValidateTreeData: boolean;
var
  i: Integer;
  TabsValid: Boolean;
  TabNode: TTreeNode;
  j: Integer;
  PanesValid: Boolean;
  PaneNode: TTreeNode;
  k: Integer;
  ItemsValid: Boolean;
  ItemNode: TTreeNode;
begin
  Result := false;
  if (FToolbar = nil) or (FDesigner = nil) then
    exit;

  i := 0;
  TabsValid := true;
  TabNode := tvStructure.Items.GetFirstNode;

  while (i < FToolbar.Tabs.Count) and TabsValid do
  begin
    TabsValid := TabsValid and (TabNode <> nil);
    if TabsValid then
      TabsValid := TabsValid and (TabNode.Data = FToolbar.Tabs[i]);
    if TabsValid then
    begin
      j := 0;
      PanesValid := true;
      PaneNode := TabNode.GetFirstChild;
      while (j < FToolbar.Tabs[i].Panes.Count) and PanesValid do
      begin
        PanesValid := PanesValid and (PaneNode <> nil);
        if PanesValid then
          PanesValid := PanesValid and (PaneNode.Data = FToolbar.Tabs[i].Panes[j]);
        if PanesValid then
        begin
          k := 0;
          ItemsValid := true;
          ItemNode := PaneNode.GetFirstChild;
          while (k < FToolbar.Tabs[i].Panes[j].Items.Count) and ItemsValid do
          begin
            ItemsValid := ItemsValid and (ItemNode <> nil);
            if ItemsValid then
              ItemsValid := ItemsValid and (ItemNode.Data = FToolbar.Tabs[i].Panes[j].Items[k]);
            if ItemsValid then
            begin
              inc(k);
              ItemNode := ItemNode.GetNextSibling;
            end;
          end;

          // Important! You need to make sure that there are no extra items in the tree!
          ItemsValid := ItemsValid and (ItemNode = nil);
          PanesValid := PanesValid and ItemsValid;
        end;

        if PanesValid then
        begin
          inc(j);
          PaneNode := PaneNode.GetNextSibling;
        end;
      end;

      // Important! You need to make sure that there are no extra items in the tree!
      PanesValid := PanesValid and (PaneNode = nil);
      TabsValid := TabsValid and PanesValid;
    end;

    if TabsValid then
    begin
      inc(i);
      TabNode := TabNode.GetNextSibling;
    end;
  end;

  // Important! You need to make sure that there are no extra items in the tree!
  TabsValid := TabsValid and (TabNode = nil);
  Result := TabsValid;
end;

procedure TfrmEditWindow.UpdatePPI;
begin
  tbToolbar.Images := nil;
  if ScreenInfo.PixelsPerInchY >= 180 then begin
    ActionList.Images := ilActionImages_200;
    tbToolbar.Images := ilActionImages_200;
    tvStructure.Images := ilTreeImages_200;
    pmAddItem.Images := ilActionImages_200;
    pmStructure.Images := ilActionImages_200;
  end else
  if ScreenInfo.PixelsPerInchY >= 135 then begin
    ActionList.Images := ilActionImages_150;
    tbToolbar.Images := ilActionImages_150;
    tvStructure.Images := ilTreeImages_150;
    pmAddItem.Images := ilActionImages_150;
    pmStructure.Images := ilActionImages_150;
  end else begin
    ActionList.Images := ilActionImages;
    tbToolbar.Images := ilActionImages;
    tvStructure.Images := ilTreeImages;
    pmAddItem.Images := ilActionImages;
    pmStructure.Images := ilActionImages;
  end;
  tbToolbar.ButtonHeight := tbToolbar.Images.Height + 8;
end;

end.
