// included by stdctrls.pp
{
 *****************************************************************************
 *                                                                           *
 *  This file is part of the Lazarus Component Library (LCL)                 *
 *                                                                           *
 *  See the file COPYING.LCL, included in this distribution,                 *
 *  for details about the copyright.                                         *
 *                                                                           *
 *  This program is distributed in the hope that it will be useful,          *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
 *                                                                           *
 *****************************************************************************
}
{ if not HandleAllocated then
    FItems contains a TExtendedStringList
  else
    FItems contains an interface specific TStrings descendent
}

type
  TCustomListBoxItemFlag = (clbiSelected);
  TCustomListBoxItemFlags = set of TCustomListBoxItemFlag;
  
  TCustomListBoxItemRecord = record
    TheObject: TObject;
    Flags: TCustomListBoxItemFlags;
  end;
  PCustomListBoxItemRecord = ^TCustomListBoxItemRecord;


function GetListBoxItemRecord(ListBoxInternalItems: TStrings;
  Index: integer): PCustomListBoxItemRecord;
begin
  Result:=PCustomListBoxItemRecord(
                      TExtendedStringList(ListBoxInternalItems).Records[Index]);
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.CreateHandle                                        }
{------------------------------------------------------------------------------}
procedure TCustomListBox.CreateHandle;
var
  NewStrings : TStrings;
  i: integer;
begin
  //writeln('[TCustomListBox.CreateHandle] A ',FItems.ClassName);
  inherited CreateHandle;
  //writeln('[TCustomListBox.CreateHandle] B ',FItems.ClassName);
  // create
  CNSendMessage(LM_SETBORDER, Self, nil);
  UpdateSelectionMode;
  UpdateSorted;

  // fetch the interface item list
  NewStrings:= TStrings(Pointer(CNSendMessage(LM_GETITEMS, Self, nil)));
  // copy the items (text+objects)
  NewStrings.Assign(Items);
  // copy items attributes
  for i:=0 to Items.Count-1 do begin
    if clbiSelected in GetListBoxItemRecord(FItems,i)^.Flags then
      SendItemSelected(i,True);
  end;
  // free old items
  FItems.Free;
  
  // new item list is the interface item list
  FItems:= NewStrings;
  //writeln('[TCustomListBox.CreateHandle] END ',FItems.ClassName);
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.DestroyHandle                                        }
{------------------------------------------------------------------------------}
procedure TCustomListBox.DestroyHandle;
var
  NewStrings : TStrings;
  i, Cnt: integer;
begin
  //writeln('[TCustomListBox.DestroyHandle] A ',FItems.ClassName);
  // create internal item list
  NewStrings:= TExtendedStringList.Create(SizeOf(TCustomListBoxItemRecord));
  // copy items (text+objects) from the interface items list
  NewStrings.Assign(Items);
  // copy items attributes
  Cnt:=Items.Count;
  for i:=0 to Cnt-1 do begin
    if Selected[i] then
      Include(GetListBoxItemRecord(NewStrings,i)^.Flags,clbiSelected);
  end;
  // free the interface items list
  FItems.Free;
  // new item list is the internal item list
  FItems:= NewStrings;
  //writeln('[TCustomListBox.DestroyHandle] B ',FItems.ClassName);
  inherited DestroyHandle;
  //writeln('[TCustomListBox.DestroyHandle] END ',FItems.ClassName);
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.SetBorderStyle                                      }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetBorderStyle(Val : TBorderStyle);
begin
  if FBorderStyle <> Val then begin
    FBorderStyle:= Val;
    if HandleAllocated then CNSendMessage(LM_SETBORDER, Self, nil);
  end;
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.UpdateSelectionMode                                 }
{------------------------------------------------------------------------------}
procedure TCustomListBox.UpdateSelectionMode;
var
  Msg : TLMSetSelMode;
begin
  if not HandleAllocated then exit;
  Msg.ExtendedSelect:= ExtendedSelect;
  Msg.MultiSelect:= MultiSelect;
  CNSendMessage(LM_SETSELMODE, Self, @Msg);
end;

{------------------------------------------------------------------------------
  procedure TCustomListBox.UpdateSorted;
------------------------------------------------------------------------------}
procedure TCustomListBox.UpdateSorted;
var AMessage : TLMSort;
begin
  if not HandleAllocated then exit;
  with AMessage do begin
    Msg:= LM_SORT;
    List:= Items;
    IsSorted:= FSorted;
  end;
  CNSendMessage(LM_SORT, Self, @AMessage);
end;

{------------------------------------------------------------------------------
  procedure TCustomListBox.LMDrawListItem(var TheMessage: TLMDrawListItem);

  Handler for custom drawing items.
 ------------------------------------------------------------------------------}
procedure TCustomListBox.LMDrawListItem(var TheMessage: TLMDrawListItem);
begin
  with TheMessage.DrawListItemStruct^ do
  begin
    FCanvas.Handle := DC;
    if Font<>nil then
      FCanvas.Font := Font;
    if Brush<>nil then
      FCanvas.Brush := Brush;
    if (ItemID >= 0) and (odSelected in ItemState) then
    begin
      FCanvas.Brush.Color := clHighlight;
      FCanvas.Font.Color := clHighlightText
    end;
    DrawItem(ItemID, Area, ItemState);
    if odFocused in ItemState then
      {DrawFocusRect(hDC, rcItem)};
    FCanvas.Handle := 0;
  end;
end;

{------------------------------------------------------------------------------
  procedure TCustomListBox.SendItemSelected(Index: integer; IsSelected: boolean);

  Tell the interface whether an item is selected.
------------------------------------------------------------------------------}
procedure TCustomListBox.SendItemSelected(Index: integer; IsSelected: boolean);
var
  Msg : TLMSetSel;
begin
  if HandleAllocated then begin
    Msg.Index:= Index;
    Msg.Selected:= IsSelected;
    CNSendMessage(LM_SETSEL, Self, @Msg);
  end;
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.SetExtendedSelect                                   }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetExtendedSelect(Val : boolean);
begin
  if Val <> FExtendedSelect then begin
    FExtendedSelect:= Val;
    UpdateSelectionMode;
  end;
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.SetMultiSelect                                      }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetMultiSelect(Val : boolean);
begin
  if Val <> FMultiSelect then begin
    FMultiSelect:= Val;
    UpdateSelectionMode;
  end;
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.SetSelected                                         }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetSelected(Index : integer; Val : boolean);
begin
  if (Index < 0) or (Index >= Items.Count) then
    raise Exception.Create('List index out of bounds');
  //writeln('TCustomListBox.SetSelected A ',Items.Count);
  if HandleAllocated then begin
    //writeln('TCustomListBox.SetSelected B ',Items.Count);
    SendItemSelected(Index,Val);
    //writeln('TCustomListBox.SetSelected END ',Items.Count);
  end else begin
    if Val then
      Include(GetListBoxItemRecord(FItems,Index)^.Flags,clbiSelected)
    else
      Exclude(GetListBoxItemRecord(FItems,Index)^.Flags,clbiSelected)
  end;
end;

{------------------------------------------------------------------------------}
{ function TCustomListBox.GetSelected                                          }
{------------------------------------------------------------------------------}
function TCustomListBox.GetSelected(Index : integer) : boolean;
begin
  if (Index < 0) or (Index >= Items.Count) then
    raise Exception.Create('TCustomListBox.GetSelected: index '+IntToStr(Index)
      +' out of bound. Count='+IntToStr(Items.Count));
  if HandleAllocated then
    Result:= (CNSendMessage(LM_GETSEL, Self, @Index) >= 0)
  else
    Result:=clbiSelected in GetListBoxItemRecord(FItems,Index)^.Flags;
end;

{------------------------------------------------------------------------------}
{ function TCustomListBox.GetSelCount                                          }
{------------------------------------------------------------------------------}
function TCustomListBox.GetSelCount : integer;
begin
  if HandleAllocated then
    Result:= CNSendMessage(LM_GETSELCOUNT, Self, nil)
  else
    Result:=0;
end;

function TCustomListBox.GetItemHeight: Integer;
begin
  Result := FItemHeight;
end;

procedure TCustomListBox.SetItemHeight(Value: Integer);
begin
  if (FItemHeight <> Value) and (Value > 0) then
    FItemHeight := Value;
end;

{------------------------------------------------------------------------------}
{ procedure TCustomListBox.SetSorted                                          }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetSorted(Val : boolean);
begin
  if Val <> FSorted then begin
    FSorted:= Val;
    UpdateSorted;
  end;
end;

{------------------------------------------------------------------------------}
{  procedure TCustomListBox.SetStyle                                }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetStyle(Val : TListBoxStyle);
begin
  if Val <> FStyle then begin
    FStyle:= Val;
  end;
end;

procedure TCustomListBox.DrawItem(Index: Integer; ARect: TRect;
  State: TOwnerDrawState);
{var
  Flags: Longint;
  Data: String;}
begin
  if Assigned(FOnDrawItem) then
    FOnDrawItem(Self, Index, ARect, State)
  else if not (odPainted in State) then
  begin
    FCanvas.FillRect(ARect);
    if (Index>=0) and (Index < Items.Count) then
    begin
      {Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
      if not UseRightToLeftAlignment then
        Inc(Rect.Left, 2)
      else
        Dec(Rect.Right, 2);
      Data := '';
      if (Style in [lbVirtual, lbVirtualOwnerDraw]) then
        Data := DoGetData(Index)
      else
        Data := Items[Index];
      DrawText(FCanvas.Handle, PChar(Data), Length(Data), Rect, Flags);}
    end;
  end;
end;

{------------------------------------------------------------------------------}
{  function TCustomListBox.SetItems                                   }
{------------------------------------------------------------------------------}
procedure TCustomListBox.SetItems(Value : TStrings);
begin
  if (Value <> FItems) then begin
//writeln('[TCustomListBox.SetItems] A FItems=',FItems.ClassName,' Value=',Value.ClassName);
    FItems.Assign(Value);
  end;
end;

{------------------------------------------------------------------------------}
{  function TCustomListBox.Create                                      }
{------------------------------------------------------------------------------}
constructor TCustomListBox.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  fCompStyle := csListBox;

  FBorderStyle:= bsSingle;
  FItems := TExtendedStringList.Create(SizeOf(TCustomListBoxItemRecord));
  FItemIndex:=-1;
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;
  SetBounds(1, 1, 100, 25);
end;

{------------------------------------------------------------------------------}
{  function TCustomListBox.Destroy                                      }
{------------------------------------------------------------------------------}
destructor TCustomListBox.Destroy;
begin
  FCanvas.Free;
  FCanvas:=nil;
  inherited Destroy;
  FItems.Free;
end;

function TCustomListBox.GetItemIndex : integer;
begin
//writeln('[TCustomListBox.GetItemIndex] A ',FItems.ClassName);
  if HandleAllocated then begin
    Result:= CNSendMessage(LM_GETITEMINDEX, Self, nil);
    FItemIndex:=Result;
  end else
    Result:=FItemIndex;
//writeln('[TCustomListBox.GetItemIndex] END ');
end;

procedure TCustomListBox.SetItemIndex(Val : integer);
begin
  if (Val < 0) or (Val >= FItems.Count) then raise Exception.Create('Out of bounds');
//writeln('[TCustomListBox.SetItemIndex] A ',FItems.ClassName,' ',Val);
  FItemIndex:=Val;
  if HandleAllocated then
    CNSendMessage(LM_SETITEMINDEX, Self, Pointer(Val));
//writeln('[TCustomListBox.SetItemIndex] END ',FItems.ClassName);
end;

{------------------------------------------------------------------------------}
{  procedure TCustomListBox.Clear                                            }
{------------------------------------------------------------------------------}
procedure TCustomListBox.Clear;
begin
  FItems.Clear;
end;


// back to stdctrls.pp
