{***************************************************************************}
{ TMS IntraWeb Component Pack Pro                                           }
{ for Delphi & C++Builder                                                   }
{                                                                           }
{ written by TMS Software                                                   }
{            copyright © 2014                                               }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{                                                                           }
{ The source code is given as is. The author is not responsible             }
{ for any possible damage done due to the use of this code.                 }
{ The component can be freely used in any application. The complete         }
{ source code remains property of the author and may not be distributed,    }
{ published, given or sold in any form as such. No parts of the source      }
{ code can be included in any other component or application without        }
{ written authorization of the author.                                      }
{***************************************************************************}

unit IWListEditor;

{$I TMSDEFS.INC}

interface

uses
  Windows, Graphics, Controls, Forms,
  {$IFDEF DELPHI6_LVL}
  Types,
  {$ENDIF}
  Classes,
  IWControl, IWTypes, IWRenderContext, IWHTMLTag, IWXMLTag, IWTMSBase, IWColor,
  IWFont;

type
  TTIWListEditor = class;
  TIWEditListItems = class;

  TIWAsyncItemEvent = procedure(Sender: TObject; EventParams: TStringList; Index: integer) of object;
  TIWAsyncItemEditEvent = procedure(Sender: TObject; EventParams: TStringList; Index: integer; var AValue: string; var AImage; var Allow: boolean) of object;
  TIWAsyncItemInsertEvent = procedure(Sender: TObject; EventParams: TStringList; var AValue: string; var AImage: string; var Allow: boolean) of object;

  TIWItemAppearance = class(TPersistent)
  private
    FBorderColor: TIWColor;
    FTextColor: TIWColor;
    FColorTo: TIWColor;
    FColorFrom: TIWColor;
    FRounding: integer;
    FOnChange: TNotifyEvent;
    procedure SetBorderColor(const Value: TIWColor);
    procedure SetColorFrom(const Value: TIWColor);
    procedure SetColorTo(const Value: TIWColor);
    procedure SetRounding(const Value: integer);
    procedure SetTextColor(const Value: TIWColor);
  protected
    procedure DoChange; virtual;
  public
    constructor Create;
    procedure Assign(Source: TPersistent); override;
  published
    property ColorFrom: TIWColor read FColorFrom write SetColorFrom;
    property ColorTo: TIWColor read FColorTo write SetColorTo;
    property BorderColor: TIWColor read FBorderColor write SetBorderColor;
    property Rounding: integer read FRounding write SetRounding default 6;
    property TextColor: TIWColor read FTextColor write SetTextColor;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  TIWAppearance = class(TPersistent)
  private
    FNormal: TIWItemAppearance;
    FSelected: TIWItemAppearance;
    FOnChange: TNotifyEvent;
    FShowDeleteButton: boolean;
    FDeleteButtonHint: string;
    FItemHeight: integer;
    FDeleteButtonColor: TIWColor;
    FDeleteButtonImageURL: string;
    FBGColor: TIWColor;
    procedure SetNormal(const Value: TIWItemAppearance);
    procedure SetSelected(const Value: TIWItemAppearance);
    procedure ItemAppearanceChange(Sender: TObject);
    procedure SetShowDeleteButton(const Value: boolean);
    procedure SetDeleteButtonHint(const Value: string);
    procedure SetItemHeight(const Value: integer);
    procedure SetDeleteButtonColor(const Value: TIWColor);
    procedure SetDeleteButtonImageURL(const Value: string);
    procedure SetBGColor(const Value: TIWColor);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property BGColor: TIWColor read FBGColor write SetBGColor default clWebWhite;
    property Normal: TIWItemAppearance read FNormal write SetNormal;
    property Selected: TIWItemAppearance read FSelected write SetSelected;
    property ShowDeleteButton: boolean read FShowDeleteButton write SetShowDeleteButton default true;
    property DeleteButtonHint: string read FDeleteButtonHint write SetDeleteButtonHint;
    property DeleteButtonColor: TIWColor read FDeleteButtonColor write SetDeleteButtonColor default clWebDarkGray;
    property DeleteButtonImageURL: string read FDeleteButtonImageURL write SetDeleteButtonImageURL;
    property ItemHeight: integer read FItemHeight write SetItemHeight default 24;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  TIWEditListItem = class(TCollectionItem)
  private
    FOwner: TIWEditListItems;
    FCaption: string;
    FName: string;
    FTag: integer;
    FImageURL: string;
    FValue: string;
    FHint: string;
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    function GetOwner: TPersistent; override;
  published
    property Caption: string read FCaption write FCaption;
    property Hint: string read FHint write FHint;
    property ImageURL: string read FImageURL write FImageURL;
    property Name: string read FName write FName;
    property Tag: integer read FTag write FTag default 0;
    property Value: string read FValue write FValue;
  end;

  TIWEditListItems = class(TCollection)
  private
    FOwner: TTIWListEditor;
    function GetItem(Index: Integer): TIWEditListItem;
    procedure SetItem(Index: Integer; const Value: TIWEditListItem);
  protected
    procedure Update(Item: TCollectionItem); override;
  public
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    constructor Create(AOwner: TTIWListEditor);
    function Add: TIWEditListItem;
    function GetOwner: TPersistent; override;
    property Items[Index: Integer]: TIWEditListItem read GetItem write SetItem; default;
  end;

  TTIWListEditor = class(TIWControl)
  private
    FItemIndex: integer;
    FItemValue: string;
    FItemImage: string;
    FAutoSize: boolean;
    FItems: TIWEditListItems;
    FBorderColor: TIWColor;
    FBorderWidth: integer;
    FAppearance: TIWAppearance;
    FOnAsyncItemClick: TIWAsyncItemEvent;
    FOnAsyncEditStart: TIWAsyncItemEvent;
    FOnAsyncEditEnd: TIWAsyncItemEditEvent;
    FReadOnly: boolean;
    FOnAsyncItemDelete: TIWAsyncItemEvent;
    FOnAsyncItemInsert: TIWAsyncItemInsertEvent;
    FMaxLength: integer;
    FFont: TIWFont;
    FConfirmDeleteText: string;
    FConfirmDelete: boolean;
    procedure SetAppearance(const Value: TIWAppearance);
    procedure DoAsyncItemClick(AParams: TStringList);
    procedure DoAsyncItemDelete(AParams: TStringList);
    procedure DoAsyncItemInsert(AParams: TStringList);
    procedure DoAsyncEditStart(AParams: TStringList);
    procedure DoAsyncEditEnd(AParams: TStringList);
    procedure AppearanceChange(Sender: TObject);
    procedure SetItemIndex(const Value: integer);
  protected
    procedure SetValue(const value:string); override;
    procedure Submit(const AValue: string); override;
  public
    procedure IWPaint; override;
    function RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag; override;
    function RenderAsync(AContext: TIWBaseHTMLComponentContext): TIWXMLTag; override;
    procedure AsyncUpdate();
    procedure AsyncItemAdd(AItem: TIWEditListItem);
    procedure AsyncItemDelete(AItemIndex: integer);
    procedure AsyncItemUpdate(AItem: TIWEditListItem);
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Appearance: TIWAppearance read FAppearance write SetAppearance;
    property AutoSize: boolean read FAutoSize write FAutoSize default false;
    property BorderColor: TIWColor read FBorderColor write FBorderColor default clNone;
    property BorderWidth: integer read FBorderWidth write FBorderWidth default 1;
    property ConfirmDelete: boolean read FConfirmDelete write FConfirmDelete default false;
    property ConfirmDeleteText: string read FConfirmDeleteText write FConfirmDeleteText;
    property Enabled;
    property Font: TIWFont read FFont write FFont;
    property Items: TIWEditListItems read FItems write FItems;
    property ItemIndex: integer read FItemIndex write SetItemIndex default -1;
    property MaxLength: integer read FMaxLength write FMaxLength default 0;
    property ReadOnly: boolean read FReadOnly write FReadOnly default false;
    property TabOrder;

    property OnAsyncItemClick: TIWAsyncItemEvent read FOnAsyncItemClick write FOnAsyncItemClick;
    property OnAsyncEditStart: TIWAsyncItemEvent read FOnAsyncEditStart write FOnAsyncEditStart;
    property OnAsyncEditEnd: TIWAsyncItemEditEvent read FOnAsyncEditEnd write FOnAsyncEditEnd;
    property OnAsyncItemDelete: TIWAsyncItemEvent read FOnAsyncItemDelete write FOnAsyncItemDelete;
    property OnAsyncItemInsert: TIWAsyncItemInsertEvent read FOnAsyncItemInsert write FOnAsyncItemInsert;
  end;


implementation

uses
  IWApplication, SysUtils;

{ TTIWListEditor }
function TTIWListEditor.RenderAsync(
  AContext: TIWBaseHTMLComponentContext): TIWXMLTag;
var
  js: string;
begin
  Result := TIWXMLTag.CreateTag('control');
  try
    Result.AddStringParam('id', HTMLName);
    Result.AddStringParam('type', 'TTIWListEditor');
    RenderAsyncCommonProperties(AContext, Result);

    js := js
    + HTMLName + 'enabled = ' + BoolToStr(Enabled) + ';'#13
    + HTMLName + 'readonly = ' + BoolToStr(ReadOnly) + ';'#13;

    if Enabled and not ReadOnly then
      js := js + 'document.getElementById("' + HTMLName + 'Input").className = "";'
    else
      js := js + 'document.getElementById("' + HTMLName + 'Input").className = "' + HTMLName + 'Hidden";';

    GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
  except
    FreeAndNil(Result);
  end;
end;

procedure TTIWListEditor.AppearanceChange(Sender: TObject);
begin
  Invalidate;
end;

procedure TTIWListEditor.AsyncItemAdd(AItem: TIWEditListItem);
var
  js: string;
begin
  js := js
    + ' ' + HTMLName + 'RenderItem("'
    + StringReplace(AItem.Caption, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.Value, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.Hint, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.ImageURL, '"', '\"', [rfReplaceAll]) + '");'
    + ' ' + HTMLName + 'ResizeInput();';

  GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
end;

procedure TTIWListEditor.AsyncItemDelete(AItemIndex: integer);
var
  js: string;
begin
  js := js + ' ' + HTMLName + 'AsyncDeleteItem(' + IntToStr(AItemIndex) + ');';
  GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
  AsyncUpdate;
  FItemIndex := -1
end;

procedure TTIWListEditor.AsyncItemUpdate(AItem: TIWEditListItem);
var
  js: string;
begin
  js := js
    + ' ' + HTMLName + 'AsyncUpdateItem('
    + IntToStr(AItem.Index) + ', "'
    + StringReplace(AItem.Caption, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.Value, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.Hint, '"', '\"', [rfReplaceAll]) + '", "'
    + StringReplace(AItem.ImageURL, '"', '\"', [rfReplaceAll]) + '");'
    + ' ' + HTMLName + 'ResizeInput();';

  GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
end;

procedure TTIWListEditor.AsyncUpdate;
var
  js: string;
  I: integer;
begin
  js := js + ' ' + HTMLName + 'ClearItems();';
  GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');

  for I := 0 to Items.Count - 1  do
    AsyncItemAdd(Items[I]);
end;

constructor TTIWListEditor.Create(AOwner: TComponent);
begin
  inherited;
  FAutoSize := false;
  FBorderColor := clNone;
  FBorderWidth := 1;
  FConfirmDelete := false;
  FConfirmDeleteText := 'Do you want to delete the selected item?';
  FMaxLength := 0;
  FReadOnly := false;
  FItems := TIWEditListItems.Create(Self);
  FItemIndex := -1;
  FFont := TIWFont.Create;
  FFont.FontName := 'Arial';

  FAppearance := TIWAppearance.Create;
  FAppearance.OnChange := AppearanceChange;

  FAppearance.Normal.ColorFrom := RGB(220,230,248);
  FAppearance.Normal.ColorTo := RGB(189,207,241);
  FAppearance.Normal.BorderColor := RGB(120,133,215);
  FAppearance.Normal.TextColor := clBlack;

  FAppearance.Selected.ColorFrom := RGB(115,163,230);
  FAppearance.Selected.ColorTo := RGB(35,110,216);
  FAppearance.Selected.BorderColor := RGB(35,110,216);
  FAppearance.Selected.TextColor := clWhite;

  Enabled := true;
  Height := 50;
  Width := 200;
  {$IFNDEF TMSIW121}
  SetRenderSize(true);
  {$ENDIF}
end;

destructor TTIWListEditor.Destroy;
begin
  inherited;
  FAppearance.Free;
  FItems.Free;
  FFont.Free;
end;

procedure TTIWListEditor.DoAsyncEditEnd(AParams: TStringList);
var
  it: TIWEditListItem;
  allow: boolean;
begin
  it := Items[FItemIndex];
  FItemImage := it.ImageURL;

  if Assigned(OnAsyncEditEnd) then
    OnAsyncEditEnd(Self, AParams, FItemIndex, FItemValue, FItemImage, allow);

  if allow then
  begin
    it.Caption := FItemValue;
    it.ImageURL := FItemImage;

    AsyncItemUpdate(it);
  end;
end;

procedure TTIWListEditor.DoAsyncEditStart(AParams: TStringList);
begin
  if Assigned(OnAsyncEditStart) then
    OnAsyncEditStart(Self, AParams, FItemIndex);
end;

procedure TTIWListEditor.DoAsyncItemClick(AParams: TStringList);
begin
  if Assigned(OnAsyncItemClick) then
    OnAsyncItemClick(Self, AParams, FItemIndex);
end;

procedure TTIWListEditor.DoAsyncItemDelete(AParams: TStringList);
begin
  if Assigned(OnAsyncItemDelete) then
    OnAsyncItemDelete(Self, AParams, FItemIndex);

  Items[FItemIndex].Free;

  AsyncUpdate;
end;

procedure TTIWListEditor.DoAsyncItemInsert(AParams: TStringList);
var
  it: TIWEditListItem;
  allow: boolean;
begin
  allow := true;

  if Assigned(OnAsyncItemInsert) then
    OnAsyncItemInsert(Self, AParams, FItemValue, FItemImage, allow);

  if allow then
  begin
    it := Items.Add;
    it.Caption := FItemValue;
    it.ImageURL := FItemImage;

    AsyncItemAdd(it);
  end;
end;

procedure TTIWListEditor.IWPaint;
var
  r : TRect;
begin
  Canvas.Font.Name := Font.FontName;
  Canvas.Font.Size := Font.Size;
  Canvas.Font.Color := Font.Color;
  Canvas.Font.Style := Font.Style;

  with Canvas do
  begin

    r.Left := 0;
    r.Top := 0;
    r.Right := Width;
    r.Bottom := Height;

    Pen.Color := clWebLightGray;
    Pen.Width := 1;
    Rectangle(0,0, Width,Height);

    Brush.Style := bsClear;
    TextOut(Width div 2 - TextWidth(Name) div 2,Height div 2 - TextHeight(Name) div 2,Name);
  end;
end;         

procedure TTIWListEditor.SetAppearance(const Value: TIWAppearance);
begin
  FAppearance.Assign(Value);
end;

procedure TTIWListEditor.SetItemIndex(const Value: integer);
begin
  if FItemIndex <> Value then
  begin
    if (Value >= 0) and (Value < Items.Count) then
      FItemIndex := Value
    else
      FItemIndex := -1;
  end;
end;

procedure TTIWListEditor.SetValue (const value: string);
var
  A: TStringList;
begin
  inherited;

  if value <> '' then
  begin
    FItemImage := '';

    if Pos('^', value) > 0 then
    begin
     A := TStringList.Create;
     try
       IWTMSBase.Split('^', value, A);

       if A.Count > 0 then
       begin
        if not (A[0] = '') then
          FItemIndex := StrToInt(A[0]);
       end;

       if A.Count > 1 then
        FItemValue := A[1];

     finally
      A.Free;
     end;
    end;
  end;
end;

procedure TTIWListEditor.Submit(const AValue: string);
begin
  inherited;
end;

function TTIWListEditor.RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag;
var
  htmlres, ajaxitemclick, ajaxeditstart, ajaxeditend, ajaxdelete, ajaxinsert: string;
  I, padding, itemheight: Integer;
begin
  padding := 3;
  itemheight := Appearance.ItemHeight - (padding * 2) - 2;

  //------------
  //Async Events
  //------------
  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoAsyncItemClick', DoAsyncItemClick);
  ajaxitemclick := 'processAjaxEvent(''onClick'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoAsyncItemClick' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoAsyncItemDelete', DoAsyncItemDelete);
  ajaxdelete := 'processAjaxEvent(''onDelete'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoAsyncItemDelete' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoAsyncItemInsert', DoAsyncItemInsert);
  ajaxinsert := 'processAjaxEvent(''onInsert'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoAsyncItemInsert' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoAsyncEditStart', DoAsyncEditStart);
  ajaxeditstart := 'processAjaxEvent(''onEditStart'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoAsyncEditStart' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoAsyncEditEnd', DoAsyncEditEnd);
  ajaxeditend := 'processAjaxEvent(''onEditEnd'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoAsyncEditEnd' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  //------------
  //JavaScript
  //------------
  htmlres := '<script>'#13
   + ' var ' + HTMLName + 'autosize = ' + BoolToStr(Autosize) + ';'#13
   + ' var ' + HTMLName + 'enabled = ' + BoolToStr(Enabled) + ';'#13
   + ' var ' + HTMLName + 'readonly = ' + BoolToStr(ReadOnly) + ';'#13
   + ' var ' + HTMLName + 'delete = ' + BoolToStr(Appearance.ShowDeleteButton) + ';'#13
   + ' var ' + HTMLName + 'deletehint = "' + Appearance.DeleteButtonHint + '";'#13
   + ' var ' + HTMLName + 'deleteimg = "' + Appearance.DeleteButtonImageURL + '";'#13
   + ' var ' + HTMLName + 'count = 0;'#13
   + ' var ' + HTMLName + 'selitem = null;'#13
   + ' var ' + HTMLName + 'editem = null;'#13
   + ' var ' + HTMLName + 'index = ' + IntToStr(ItemIndex) + ';'#13
   + ' var ' + HTMLName + 'fromedit = false;'#13
   + ' var ' + HTMLName + 'cancel = false;'#13;

   if MaxLength > 0 then
     htmlres := htmlres
       + ' var ' + HTMLName + 'maxlen = "maxlength=''' + IntToStr(MaxLength) + '''";'#13
   else
     htmlres := htmlres
       + ' var ' + HTMLName + 'maxlen = "";'#13;

  htmlres := htmlres
   + 'function ' + HTMLName + 'StripHTML(html){'#13
   + ' html = html.replace(/</g, "&lt;");'#13
   + ' html = html.replace(/>/g, "&gt;");'#13
   + ' return html;'#13
   + '}'

   + 'function ' + HTMLName + 'RenderItem(caption, value, hint, image){'#13
   + ' caption = ' + HTMLName + 'StripHTML(caption);'#13
   + ' value = ' + HTMLName + 'StripHTML(value);'#13
   + ' items = document.getElementById("' + HTMLName + 'Items");'#13
   + ' classname = "' + HTMLName + 'Item";'#13
   + ' if (' + HTMLName + 'index == ' + HTMLName + 'count)'#13
   + ' classname = "' + HTMLName + 'Item ' + HTMLName + 'Selected";'#13
   + ' imgclassname = "' + HTMLName + 'Img";'#13
   + ' if (image == "")'#13
   + ' imgclassname = "' + HTMLName + 'Img ' + HTMLName + 'Hidden";'#13

   + ' newitem = "<div id=''' + HTMLName + 'Item" + ' + HTMLName + 'count + "'' '
   + 'tabindex=''0'' class=''" + classname + "'' title=''" + hint + "'' '
   + 'onClick=''return ' + HTMLName + 'EditItem(event, " + ' + HTMLName + 'count + ", true);'' '
   + 'onMouseUp=''event.stopPropagation();'' '
   + 'onKeyUp=''return ' + HTMLName + 'EditItem(event, " + ' + HTMLName + 'count + ", false);'''
   + '>";'#13

   + ' newitem = newitem + "<img id=''' + HTMLName + 'Image" + ' + HTMLName + 'count + "'' '
   + 'class=''" + imgclassname + "'' src=''" + image + "''>";'#13

   + ' newitem = newitem + "<span id=''' + HTMLName + 'Caption" + ' + HTMLName + 'count + "''>'
   + '" + caption + "</span>";'#13

   + ' newitem = newitem + "<span style=''display:none;'' id=''' + HTMLName + 'Value" + ' + HTMLName + 'count + "''>'
   + '" + value + "</span>";'#13

   + ' if (' + HTMLName + 'delete == "-1") {'#13
   + '  if (' + HTMLName + 'deleteimg == "") {'#13
   + '   newitem = newitem + "<span id=''' + HTMLName + 'Delete" + ' + HTMLName + 'count + "'' '
   + 'class=''' + HTMLName + 'Delete'' title=''" + ' + HTMLName + 'deletehint + "'' '
   + 'onMouseUp=''' + HTMLName + 'DeleteItem(event, " + ' + HTMLName + 'count + ");'' '
   + '>&times</span>";'#13
   + '  } else {'#13
   + '   newitem = newitem + "<img id=''' + HTMLName + 'Delete" + ' + HTMLName + 'count + "'' '
   + 'class=''' + HTMLName + 'DelImg'' title=''" + ' + HTMLName + 'deletehint + "'' '
   + 'onMouseUp=''' + HTMLName + 'DeleteItem(event, " + ' + HTMLName + 'count + ");'' '
   + 'src=''" + ' + HTMLName + 'deleteimg + "''>";'#13
   + '  }'#13
   + ' }'#13

   + ' newitem = newitem + "</div>";'#13

   + ' newitem = newitem + "<input id=''' + HTMLName + 'Edit" + ' + HTMLName + 'count + "'' '
   + 'class=''' + HTMLName + 'Edit'' " + ' + HTMLName + 'maxlen + " '
   + 'style=''display:none;width:50px;'' type=''text'' '
   + 'onKeyDown=''return ' + HTMLName + 'EndEdit(event);'' '
   + 'onBlur=''' + HTMLName + 'UpdateItem();'''
   + '>";'#13

   + ' ' + HTMLName + 'count++'#13

   + ' items.innerHTML = items.innerHTML + newitem;'#13
   + '}'#13

   + 'function ' + HTMLName + 'EditItem(e, index, mouse){'#13

   + ' event.stopPropagation();'#13

   + ' if (' + HTMLName + 'enabled == "0")'#13
   + '  return;'#13

   + 'index = parseInt(index);'#13
   + 'newindex = index;'#13
   + ' try { key = event.keyCode; }catch(err){ key = e.which };'#13

   + ' if (' + HTMLName + 'fromedit == true) {'#13
   + '  key = -1;'#13
   + '  ' + HTMLName + 'fromedit = false;'#13
   + ' }'#13

//   + ' if ((key == 46) || (key == 8)) {'#13 //delete, backspace
   + ' if (key == 46) {'#13 //delete
   + '  if (' + HTMLName + 'readonly == "-1")'#13
   + '   return;'#13
   + '  ' + HTMLName + 'DeleteItem(event, index);'#13
   + '  return;'#13
   + ' }'#13
   + ' else if ((key == 37) && (index >= 0)) {'#13 //left
   + '  if (index == 0) {'#13
   + '   return;'#13
   + '  } else'#13
   + '  newindex--;'#13
   + ' }'#13
   + ' else if ((key == 39) && (index < ' + HTMLName + 'count)) {'#13 //right
   + '  if (index + 1 == ' + HTMLName + 'count) {'#13
   + '   ' + HTMLName + 'FocusEdit();'#13
   + '   ' + HTMLName + 'index = -1;'#13
   + '   return;'#13
   + '  } else'#13
   + '   newindex++;'#13
   + ' }'#13
   + ' else if (key == 35) {'#13 //end
   + '  if (' + HTMLName + 'readonly == "-1")'#13
   + '   newindex = ' + HTMLName + 'count - 1;'#13
   + '  else {'#13
   + '   ' + HTMLName + 'FocusEdit();'#13
   + '   ' + HTMLName + 'index = -1;'#13
   + '   return;'#13
    + ' }'#13
   + ' }'#13
   + ' else if (key == 36) {'#13 //home
   + '   newindex = 0;'#13
   + ' }'#13
   + 'index = newindex;'#13

   + ' if (mouse) key = 13;'#13

   + ' if ((' + HTMLName + 'index == index) && ((key == 13) || (key == 113))) { '#13

   + ' if (' + HTMLName + 'readonly == "-1")'#13
   + '  return;'#13

   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + index);'#13
   + '  var ow = ' + HTMLName + 'selitem.offsetWidth;'#13
   + '  ' + HTMLName + 'selitem.style.display = "none";'#13
   + '  ' + HTMLName + 'editem = document.getElementById("' + HTMLName + 'Edit" + index);'#13
   + '  ' + HTMLName + 'editem.style.display = "none";'#13
   + '  ' + HTMLName + 'editem.style.display = "";'#13
   + '  ' + HTMLName + 'editem.style.width = ow + "px";'#13
   + '  edvalue = document.getElementById("' + HTMLName + 'Caption" + ' + HTMLName + 'index);'#13
   + '  if (edvalue) '#13
   + '  ' + HTMLName + 'editem.value = edvalue.innerHTML;'#13
   + '  ' + HTMLName + 'editem.focus();'#13
   + '  ' + HTMLName + 'editem.select();'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = index + "^" + edvalue.innerHTML;'#13
   + '  ' + ajaxeditstart + #13
   + ' } else {'#13
   + '  ' + HTMLName + 'UnSelect();'#13
   + '  ' + HTMLName + 'index = index;'#13
   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + index);'#13
   + '  ' + HTMLName + 'selitem.className = ' + HTMLName + 'selitem.className + " ' + HTMLName + 'Selected";'#13
   + '  ' + HTMLName + 'selitem.focus();'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = index + "^";'#13
   + '  ' + ajaxitemclick + #13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'UnSelect(){'#13
   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + ' + HTMLName + 'index);'#13
   + '  if (' + HTMLName + 'selitem) {'#13
   + '   ' + HTMLName + 'selitem.className = " ' + HTMLName + 'Item";'#13
   + ' }'#13
   + ' ' + HTMLName + 'index = -1;'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = "-1^";'#13
   + '}'#13

   + 'function ' + HTMLName + 'FocusEdit(){'#13
   + '  newinput = document.getElementById("' + HTMLName + 'Input");'#13
   + '  if (newinput) '#13
   + '   newinput.focus();'#13
   + '}'#13

   + 'function ' + HTMLName + 'EndEdit(e){'#13
   + ' try { key = event.keyCode; }catch(err){ key = e.which };'#13
   + ' if (key == 13) {'#13
   + ' ' + HTMLName + 'FocusEdit();'#13
   + ' } else if (key == 27) {'#13
   + '  ' + HTMLName + 'CancelEdit();'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'CancelEdit(){'#13
   + '  ' + HTMLName + 'cancel = true;'#13
   + '  ' + HTMLName + 'selitem.style.display = "";'#13
   + '  ' + HTMLName + 'editem.style.display = "none";'#13
   + '}'#13

   + 'function ' + HTMLName + 'UpdateItem(){'#13
   + ' ' + HTMLName + 'selitem.style.display = "";'#13
   + ' ' + HTMLName + 'editem.style.display = "none";'#13
   + ' caption = document.getElementById("' + HTMLName + 'Caption" + ' + HTMLName + 'index);'#13
   + ' if (! ' + HTMLName + 'cancel) { '#13
   + '  newval = ' + HTMLName + 'editem.value;'#13
   + '  newval = ' + HTMLName + 'StripHTML(newval);'#13
   + ' } else {'
   + '  newval = caption.innerHTML;'#13
   + ' ' + HTMLName + 'cancel = false;'#13
   + ' }'#13
   + ' if (newval != "") {'#13
//   + '  caption.innerHTML = newval;'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = ' + HTMLName + 'index + "^" + newval;'#13
   + '  ' + ajaxeditend + #13
   + ' } else {'
   + '  ' + HTMLName + 'DeleteItem(event, ' + HTMLName + 'index);'#13
   + ' }'#13
   + ' ' + HTMLName + 'UnSelect();'#13
   + '}'#13

   + 'function ' + HTMLName + 'AsyncUpdateItem(index, caption, value, hint, image){'#13
   + '  editem = document.getElementById("' + HTMLName + 'Item" + index);'#13
   + '  if (editem) '#13
   + '   editem.title = hint;'#13
   + '  edcaption = document.getElementById("' + HTMLName + 'Caption" + index);'#13
   + '  if (edcaption) '#13
   + '   edcaption.innerHTML = caption;'#13
   + '  edvalue = document.getElementById("' + HTMLName + 'Value" + index);'#13
   + '  if (edvalue) '#13
   + '   edvalue.innerHTML = value;'#13
   + '  edimg = document.getElementById("' + HTMLName + 'Image" + index);'#13
   + '  if (edimg) {'#13
   + '   edimg.src = image;'#13
   + '   if (image == "") '#13
   + '    edimg.className = "' + HTMLName + 'Img ' + HTMLName + 'Hidden";'#13
   + '   else'#13
   + '    edimg.className = "' + HTMLName + 'Img";'#13
   + '  }'#13
   + '}'#13

   + 'function ' + HTMLName + 'InsertKey(e, ctrl){'#13
   + ' try { key = event.keyCode; }catch(err){ key = e.which };'#13

   + ' if (key == 13)'#13
   + '   ' + HTMLName + 'InsertItem(ctrl);'#13

   + ' if (' + HTMLName + 'GetCaretPosition(ctrl) > 0)'#13
   + '  return;'#13

   + ' if (key == 37) {'#13 //left
   + '  ' + HTMLName + 'fromedit = true;'#13
   + '  insertindex = ' + HTMLName + 'count - 1;'#13
   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + insertindex);'#13
   + '  ' + HTMLName + 'selitem.className = ' + HTMLName + 'selitem.className + " ' + HTMLName + 'Selected";'#13
   + '  ' + HTMLName + 'selitem.focus();'#13
   + ' }'#13
   + ' else if (key == 36) {'#13 //home
   + '  ' + HTMLName + 'fromedit = true;'#13
   + '  insertindex = 0;'#13
   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + insertindex);'#13
   + '  ' + HTMLName + 'selitem.className = ' + HTMLName + 'selitem.className + " ' + HTMLName + 'Selected";'#13
   + '  ' + HTMLName + 'selitem.focus();'#13
   + ' }'#13

   + '}'#13

   + 'function ' + HTMLName + 'InsertItem(ctrl){'#13
   + ' if (ctrl.value != "") {'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = ' + HTMLName + 'index + "^" + ctrl.value;'#13
   + '  ' + ajaxinsert + #13
   + '  ctrl.value = "";'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'DeleteItem(event, index){'#13

   + ' if (' + HTMLName + 'enabled == "0")'#13
   + '  return;'#13

   + ' if (' + HTMLName + 'readonly == "-1")'#13
   + '  return;'#13

   + ' event.stopPropagation();'#13;

 if ConfirmDelete then
  htmlres := htmlres + 'if (confirm("' + StringReplace(ConfirmDeleteText, '"', '&quot;', [rfReplaceAll])  + '")) {'#13;

  htmlres := htmlres
   + ' items = document.getElementById("' + HTMLName + 'Items");'#13
   + ' delitem = document.getElementById("' + HTMLName + 'Item" + index);'#13
   + ' if ((items) && (delitem)) {'#13
   + '  items.removeChild(delitem);'#13
   + '  FindElem("'+HTMLName+'_INPUT").value = index + "^";'#13
   + '  ' + ajaxdelete + #13
   + '  ' + HTMLName + 'index = -1;'#13
   + '  ' + HTMLName + 'FocusEdit();'#13
   + ' }'#13;

 if ConfirmDelete then
  htmlres := htmlres + '  }'#13;

  htmlres := htmlres
   + '}'#13

   + 'function ' + HTMLName + 'AsyncDeleteItem(index){'#13
   + ' items = document.getElementById("' + HTMLName + 'Items");'#13
   + ' delitem = document.getElementById("' + HTMLName + 'Item" + index);'#13
   + ' if ((items) && (delitem)) {'#13
   + '  items.removeChild(delitem);'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'ClearItems(){'#13
   + ' items = document.getElementById("' + HTMLName + 'Items");'#13
   + ' if (items) {'#13
   + '  items.innerHTML = "";'#13
   + ' ' + HTMLName + 'count = 0;'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'SetFocus(){'#13
   + ' if (' + HTMLName + 'index == -1) {'#13
   + '  document.getElementById("' + HTMLName + 'Input").focus();'#13
//   + ' } else {'#13
//   + '  ' + HTMLName + 'selitem = document.getElementById("' + HTMLName + 'Item" + ' + HTMLName + 'index);'#13
//   + '  if (' + HTMLName + 'selitem)'#13
//   + '   ' + HTMLName + 'selitem.focus();'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'ResizeInput() {'#13
   + ' inputcon = document.getElementById("' + HTMLName + 'InputCon");'#13
   + ' outerdiv = document.getElementById("' + HTMLName + '");'#13
   + ' if ((inputcon) && (outerdiv)) {'#13
   + ' var scrollwidth = 0;'#13
   + ' if (' + HTMLName + 'autosize == "0")'#13
   + '  scrollwidth = 22;'#13
   + '  inputcon.style.width = "10px";'#13
   + '  awidth = (outerdiv.offsetWidth - inputcon.offsetLeft - scrollwidth - (' + IntToStr(padding) + ' * 4));'#13
   + '  if (awidth < 10)'#13
   + '   awidth = (outerdiv.offsetWidth - scrollwidth - (' + IntToStr(padding) + ' * 4));'#13
   + '  inputcon.style.width = awidth + "px";'#13
   + ' }'#13
   + '}'#13

   + 'function ' + HTMLName + 'GetCaretPosition(oField) {'#13
  // Initialize
   + '  var iCaretPos = 0;'#13
  // IE Support
   + '  if (document.selection) {'#13
    // Set focus on the element
   + '    oField.focus ();'#13
    // To get cursor position, get empty selection range
   + '    var oSel = document.selection.createRange ();'#13
    // Move selection start to 0 position
   + '    oSel.moveStart ("character", -oField.value.length);'#13
    // The caret position is selection length
   + '    iCaretPos = oSel.text.length;'#13
   + '  }'#13
  // Firefox support
   + '  else if (oField.selectionStart || oField.selectionStart == "0")'#13
   + '    iCaretPos = oField.selectionStart;'#13
  // Return results
   + '  return (iCaretPos);'#13
   + '}'#13

   + '</script>'#13;

  //------------
  //CSS
  //------------
  htmlres := htmlres
    + '<style>'#13
    + '#' + HTMLName + ' {'#13
    + ' background-color:' + ColorToRGBString(Appearance.BGColor) + ';'#13
    + ' border:' + IntToStr(BorderWidth) + 'px solid ' + ColorToRGBString(BorderColor) + ';'#13
    + Font.FontToStringStyle(AContext.WebApplication.Browser) + #13;

  if AutoSize then
  begin
    htmlres := htmlres
      + ' display:inline-block;'#13
      + ' height:auto!important;'#13;
  end
  else
  begin
    htmlres := htmlres
      + ' overflow:auto;'#13;
  end;

  htmlres := htmlres
    + '}'#13

    + '.' + HTMLName + 'Items {'#13
    + ' float:left;'#13
    + '}'#13

    + '.' + HTMLName + 'Hidden {'#13
    + ' display:none;'#13
    + '}'#13

    + '.' + HTMLName + 'Item {'#13
    + GradientStyle(Appearance.Normal.ColorFrom, Appearance.Normal.ColorFrom, Appearance.Normal.ColorTo, clNone, gdVertical, AContext, true)
    + ' color:' + ColorToRGBString(Appearance.Normal.TextColor) + ';'#13
	  + ' -webkit-border-radius: ' + IntToStr(Appearance.Normal.Rounding) + 'px;'#13
	  + ' -moz-border-radius: ' + IntToStr(Appearance.Normal.Rounding) + 'px;'#13
    + ' border-radius: ' + IntToStr(Appearance.Normal.Rounding) + 'px;'#13
    + ' border:1px solid ' + ColorToRGBString(Appearance.Normal.BorderColor) + ';'#13
    + ' padding:' + IntToStr(padding) + 'px;'#13
    + ' margin:' + IntToStr(padding) + 'px;'#13
    + ' float:left;'#13
    + ' cursor:pointer;'#13
    + ' outline:none;'#13
    + ' height:' + IntToStr(itemheight) + 'px;'#13
    + ' line-height:' + IntToStr(itemheight) + 'px;'#13
    + ' overflow:hidden;'#13
    + '}'#13

    + '.' + HTMLName + 'Selected {'#13
    + GradientStyle(Appearance.Selected.ColorFrom, Appearance.Selected.ColorFrom, Appearance.Selected.ColorTo, clNone, gdVertical, AContext, true)
    + ' color:' + ColorToRGBString(Appearance.Selected.TextColor) + ';'#13
    + '}'#13

    + '.' + HTMLName + 'Img {'#13
    + ' vertical-align:middle;'#13
    + ' margin-top:auto;'#13
    + ' padding-right:' + IntToStr(padding) + 'px;'#13
    + ' max-height:' + IntToStr(itemheight) + 'px;'#13
    + '}'#13

    + '.' + HTMLName + 'DelImg {'#13
    + ' vertical-align:middle;'#13
    + ' margin-top:auto;'#13
    + ' padding-left:' + IntToStr(padding * 2) + 'px;'#13
    + ' max-height:' + IntToStr(itemheight) + 'px;'#13
    + '}'#13

    + '.' + HTMLName + 'Delete {'#13
    + ' font-family:"Arial";'#13
    + ' font-weight:bold;'#13
    + ' font-size:18px;'#13
    + ' color:' + ColorToRGBString(Appearance.DeleteButtonColor) + ';'#13
    + ' padding-left:' + IntToStr(padding * 2) + 'px;'#13
    + ' float:right;'#13
    + ' height:100%;'#13
    + '}'#13

    + '.' + HTMLName + 'InputCon {'#13
    + ' float:left;'#13
    + ' width:10px;'#13
    + ' height:' + IntToStr(itemheight + (padding * 2)) + 'px;'#13
    + ' margin:' + IntToStr(padding) + 'px;'#13
    + '}'#13

    + '#' + HTMLName + 'Input {'#13
    + ' height:100%;'#13
    + ' width:100%;'#13
    + ' padding:0px;'#13
    + ' margin:0px;'#13
    + '}'#13

    + '.' + HTMLName + 'Edit {'#13
    + ' float:left;'#13
    + ' height:' + IntToStr(itemheight + (padding * 2)) + 'px;'#13
    + ' padding:' + IntToStr(padding) + 'px;'#13
    + '}'#13

    + '#' + HTMLName + ' input[type="text"] {'#13
    + ' border:0px solid transparent;'#13
    + ' background-color:' + ColorToRGBString(Appearance.BGColor) + ';'#13
    + '}'#13

    + '#' + HTMLName + ', #' + HTMLName + ' input[type="text"]:focus {'#13
    + ' outline:none;'#13
    + '}'#13
    ;

  htmlres := htmlres + '</style>'#13;

  //------------
  //HTML
  //------------
  htmlres := htmlres + '<div id="' + HTMLName + 'Items">'#13;
  htmlres := htmlres + '</div>'#13;

//  if Enabled and not ReadOnly then
  begin
    htmlres := htmlres
      + '<div id="' + HTMLName + 'InputCon" class="' + HTMLName + 'InputCon">'
      + '<input id="' + HTMLName + 'Input" tabindex="' + IntToStr(TabOrder) + '" ';

    if MaxLength > 0 then
      htmlres := htmlres
        + 'maxlength="' + IntToStr(MaxLength) + '"';

  if not Enabled or ReadOnly then
    htmlres := htmlres + ' class="' + HTMLName + 'Hidden" ';

    htmlres := htmlres + ' type="text" onFocus="' + HTMLName + 'UnSelect();" '
//    htmlres := htmlres + ' type="text" '
      + 'onBlur="return ' + HTMLName + 'InsertItem(this);" '
      + 'onKeyDown="return ' + HTMLName + 'InsertKey(event, this);"></div>'#13;
  end;

  //Insert default items
  htmlres := htmlres + '<script>'#13;

  for I := 0 to Items.Count - 1  do
  begin
    htmlres := htmlres
      + ' ' + HTMLName + 'RenderItem("'
      + Items[I].Caption + '", "'
      + Items[I].Value + '", "'
      + Items[I].Hint + '", "'
      + Items[I].ImageURL + '");'#13
  end;

  htmlres := htmlres
//    + ' ' + HTMLName + 'ResizeInput();'#13
    + '</script>'#13;

  TIWComponent40Context(AContext).AddToInitProc(HTMLName + 'ResizeInput();');

  Result := TIWHTMLTag.CreateTag('DIV');

  Result.AddStringParam('onMouseUp', HTMLName + 'SetFocus();');
  Result.Contents.AddText(htmlres);

  with Result.Contents.AddTag('INPUT') do
  begin
    AddStringParam('TYPE','hidden');
//    AddStringParam('TYPE','text');
    AddStringParam('NAME',HTMLName+'');
    AddStringParam('ID',HTMLName+'_INPUT');
  end;
end;

{ TItemAppearance }

procedure TIWItemAppearance.Assign(Source: TPersistent);
begin
  if (Source is TIWItemAppearance) then
  begin
    FBorderColor := (Source as TIWItemAppearance).BorderColor;
    FColorFrom := (Source as TIWItemAppearance).ColorFrom;
    FColorTo := (Source as TIWItemAppearance).ColorTo;
    FRounding := (Source as TIWItemAppearance).Rounding;
    FTextColor := (Source as TIWItemAppearance).TextColor;
  end;
end;

constructor TIWItemAppearance.Create;
begin
  inherited;
  FRounding := 4;
end;

procedure TIWItemAppearance.DoChange;
begin
  if Assigned(OnChange) then
    OnChange(Self);
end;

procedure TIWItemAppearance.SetBorderColor(const Value: TIWColor);
begin
  if (FBorderColor <> Value) then
  begin
    FBorderColor := Value;
    DoChange;
  end;
end;

procedure TIWItemAppearance.SetColorFrom(const Value: TIWColor);
begin
  if (FColorFrom <> Value) then
  begin
    FColorFrom := Value;
    DoChange;
  end;
end;

procedure TIWItemAppearance.SetColorTo(const Value: TIWColor);
begin
  if (FColorTo <> Value) then
  begin
    FColorTo := Value;
    DoChange;
  end;
end;

procedure TIWItemAppearance.SetRounding(const Value: integer);
begin
  if (FRounding <> Value) then
  begin
    FRounding := Value;
    DoChange;
  end;
end;

procedure TIWItemAppearance.SetTextColor(const Value: TIWColor);
begin
  if (FTextColor <> Value) then
  begin
    FTextColor := Value;
    DoChange;
  end;
end;

{ TAppearance }

procedure TIWAppearance.Assign(Source: TPersistent);
begin
  if (Source is TIWAppearance) then
  begin
    FBGColor := (Source as TIWAppearance).BGColor;
    FNormal.Assign((Source as TIWAppearance).Normal);
    FSelected.Assign((Source as TIWAppearance).Selected);
    FShowDeleteButton := (Source as TIWAppearance).ShowDeleteButton;
    FDeleteButtonHint := (Source as TIWAppearance).DeleteButtonHint;
    FDeleteButtonColor := (Source as TIWAppearance).DeleteButtonColor;
    FDeleteButtonImageURL := (Source as TIWAppearance).DeleteButtonImageURL;
    FItemHeight := (Source as TIWAppearance).ItemHeight;
  end;
end;

constructor TIWAppearance.Create;
begin
  inherited;
  FNormal := TIWItemAppearance.Create;
  FNormal.OnChange := ItemAppearanceChange;

  FSelected := TIWItemAppearance.Create;
  FSelected.OnChange := ItemAppearanceChange;

  FBGColor := clWebWhite;
  FDeleteButtonHint := 'Remove';
  FDeleteButtonColor := clWebDarkGray;
  FDeleteButtonImageURL := '';
  FShowDeleteButton := true;
  FItemHeight := 24;
end;

destructor TIWAppearance.Destroy;
begin
  FNormal.Free;
  FSelected.Free;
  inherited;
end;

procedure TIWAppearance.ItemAppearanceChange(Sender: TObject);
begin
  if Assigned(OnChange) then
    OnChange(Self);
end;

procedure TIWAppearance.SetBGColor(const Value: TIWColor);
begin
  if FBGColor <> Value then
  begin
    FBGColor := Value;
  end;
end;

procedure TIWAppearance.SetDeleteButtonColor(const Value: TIWColor);
begin
  if FDeleteButtonColor <> Value then
  begin
    FDeleteButtonColor := Value;
  end;
end;

procedure TIWAppearance.SetDeleteButtonHint(const Value: string);
begin
  if FDeleteButtonHint <> Value then
  begin
    FDeleteButtonHint := Value;
  end;
end;

procedure TIWAppearance.SetDeleteButtonImageURL(const Value: string);
begin
  if (FDeleteButtonImageURL <> Value) then
  begin
    FDeleteButtonImageURL := Value;
    ItemAppearanceChange(Self);
  end;
end;

procedure TIWAppearance.SetItemHeight(const Value: integer);
begin
  if (FItemHeight <> Value) and (Value >= 24) then
  begin
    FItemHeight := Value;
  end;
end;

procedure TIWAppearance.SetNormal(const Value: TIWItemAppearance);
begin
  FNormal.Assign(Value);
end;

procedure TIWAppearance.SetSelected(const Value: TIWItemAppearance);
begin
  FSelected.Assign(Value);
end;

procedure TIWAppearance.SetShowDeleteButton(const Value: boolean);
begin
  if FShowDeleteButton <> Value then
  begin
    FShowDeleteButton := Value;
  end;
end;

{ TIWEditListItem }

procedure TIWEditListItem.Assign(Source: TPersistent);
begin
  if Source is TIWEditListItem then
  begin
    FCaption := (Source as TIWEditListItem).Caption;
    FHint := (Source as TIWEditListItem).Hint;
    FName := (Source as TIWEditListItem).Name;
    FTag := (Source as TIWEditListItem).Tag;
    FImageURL := (Source as TIWEditListItem).ImageURL;
    FValue := (Source as TIWEditListItem).Value;
  end;
end;

constructor TIWEditListItem.Create(Collection: TCollection);
begin
  inherited;
  FCaption := 'Item ' + IntToStr(Collection.Count - 1);
  FHint := EmptyStr;
  FImageURL := EmptyStr;
  FName := EmptyStr;
  FTag := 0;
  FValue := EmptyStr;

  if Collection is TIWEditListItems then
  begin
    FOwner := Collection as TIWEditListItems;
  end;
end;

destructor TIWEditListItem.Destroy;
begin
  inherited;
end;

function TIWEditListItem.GetDisplayName: String;
begin
  if Caption <> '' then
    Result := Caption
  else
    Result := 'Item' + IntToStr(Index);
end;

function TIWEditListItem.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

{ TIWEditListItems }

function TIWEditListItems.Add: TIWEditListItem;
begin
  Result := TIWEditListItem(inherited Add);
end;

procedure TIWEditListItems.Assign(Source: TPersistent);
var
  i: Integer;
  S: TIWEditListItems;
begin
  inherited;
  S := TIWEditListItems(Source);
  Clear;
  for i := 0 to S.Count-1 do
    Add.Assign(S[i]);
end;

constructor TIWEditListItems.Create(AOwner: TTIWListEditor);
begin
  inherited Create(TIWEditListItem);
  FOwner := AOwner;
end;

destructor TIWEditListItems.Destroy;
begin
  inherited;
end;

function TIWEditListItems.GetItem(Index: Integer): TIWEditListItem;
begin
  Result := TIWEditListItem(inherited GetItem(Index));
end;

function TIWEditListItems.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

procedure TIWEditListItems.SetItem(Index: Integer;
  const Value: TIWEditListItem);
begin
  inherited SetItem(Index, Value);
end;

procedure TIWEditListItems.Update(Item: TCollectionItem);
begin
  inherited;
end;

end.
