| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 | unit ListViewColProperties;interfaceuses  Classes, ComCtrls, Contnrs;type  TCustomListViewColProperty = class(TObject)    Alignment: TAlignment;    Caption: string;    Width: Integer;    MaxWidth: Integer;    MinWidth: Integer;    Visible: Boolean;    Order: Integer;    constructor Create(AOrder: Integer);  end;type  TCustomListViewColProperties = class(TPersistent)  private    FChanged: Boolean;    FOnChange: TNotifyEvent;    FUpdating: Integer;    FProperties: TObjectList;    FCreated: Boolean;    function GetColumns: TListColumns;    function GetCount: Integer;    function GetOrderStr: string;    procedure CheckBounds(Index: Integer);    procedure SetWidthsStr(Value: string; PixelsPerInch: Integer);    function GetWidthsStr: string;    procedure SetOrderStr(Value: string);  protected    FListView: TCustomListView;    FListViewManaged: Boolean;    FConstraintsInitialized: Boolean;    function GetAlignments(Index: Integer): TAlignment;    function GetParamsStr: string; virtual;    function GetVisible(Index: Integer): Boolean;    function GetWidths(Index: Integer): Integer;    procedure SetAlignments(Index: Integer; Value: TAlignment);    procedure SetVisible(Index: Integer; Value: Boolean);    procedure SetWidths(Index: Integer; Value: Integer);    function GetCaptions(Index: Integer): string;    procedure Changed; virtual;    procedure SetCaptions(Index: Integer; Value: string); virtual;    procedure SetParamsStr(Value: string); virtual;    procedure UpdateListView;    procedure UpdateFromListView;    procedure UpdateOrderFromListView;    procedure UpdateListViewOrder;    procedure UpdateListViewMaxMinWidth;    function GetProperties(Index: Integer): TCustomListViewColProperty;    function GetIndexByOrder(Order: Integer): Integer;    function ColumnsExists: Boolean;    procedure SetRuntimeVisible(Index: Integer; Value: Boolean; SaveWidth: Boolean);    function GetColumn(Index: Integer): TListColumn;    procedure CreateProperties(ACount: Integer);    property Columns: TListColumns read GetColumns stored False;  public    constructor Create(ListView: TCustomListView; ColCount: Integer);    destructor Destroy; override;    procedure EndUpdate;    procedure BeginUpdate;    procedure ListViewWndCreated;    procedure ListViewWndDestroying;    procedure ListViewWndDestroyed;    property Count: Integer read GetCount stored False;    property Alignments[Index: Integer]: TAlignment read GetAlignments write SetAlignments;    property Captions[Index: Integer]: string read GetCaptions write SetCaptions;    property Widths[Index: Integer]: Integer read GetWidths write SetWidths;    property Visible[Index: Integer]: Boolean read GetVisible write SetVisible;    procedure RecreateColumns;    property OnChange: TNotifyEvent read FOnChange write FOnChange;    property ParamsStr: string read GetParamsStr write SetParamsStr stored False;  end; { TCustomListViewColProperties }type  TListViewColProperties = class(TCustomListViewColProperties)  published  end; { TListViewColProperties }implementationuses  SysUtils, CommCtrl, Windows, PasTools, Controls, Forms;const  DefaultListViewMaxWidth = 1000;  DefaultListViewMinWidth = 20;{ TODO : V ListView zamezit zmenu velikosti neviditelnych sloupecku }constructor TCustomListViewColProperty.Create(AOrder: Integer);begin  Alignment := taLeftJustify;  Caption := '';  Width := 50;  Visible := True;  Order := AOrder;end;  { TCustomListViewColProperties }constructor TCustomListViewColProperties.Create(  ListView: TCustomListView; ColCount: Integer);var  ACount: Integer;begin  // This contructor (and constructors of descendants)  // is only even called from implementations of  // TCustomNortonLikeListView.NewColProperties  inherited Create;  FConstraintsInitialized := False;  FCreated := False;  FUpdating := 0;  FChanged := False;  // ColCount is not 0 for file panels (TDirView and TCustomUnixDirView).  // It is 0 otherwise.  FListViewManaged := (ColCount = 0);  FListView := ListView;  FProperties := TObjectList.Create;  if FListViewManaged then ACount := GetColumns.Count    else ACount := ColCount;  CreateProperties(ACount);  if not Assigned(FListView) then    raise Exception.Create('NIL ListView pointer.');end;destructor TCustomListViewColProperties.Destroy;begin  inherited;  FProperties.Free;end;procedure TCustomListViewColProperties.SetWidthsStr(Value: string; PixelsPerInch: Integer);var  ColStr: string;  Index: Integer;  NeedInvalidate, NewVisible: Boolean;begin  Index := 0;  NeedInvalidate := False;  BeginUpdate;  try    while (Value <> '') and (Index < Count) do    begin      ColStr := CutToChar(Value, ';', True);      Widths[Index] := LoadDimension(StrToInt(CutToChar(ColStr, ',', True)), PixelsPerInch, FListView);      NewVisible := Boolean(StrToInt(CutToChar(ColStr, ',', True)));      if Visible[Index] <> NewVisible then      begin        Visible[Index] := NewVisible;        NeedInvalidate := True;      end;      Inc(Index);    end;  finally    EndUpdate;  end;  // When visibility changes (particularly while reseting layout) redraw is needed  // (Invalidate is called in SetVisible too, but maybe it has no effect there, because it is within BeginUpdate/EndUpdate)  if NeedInvalidate and FListView.HandleAllocated then    FListView.Invalidate;end;function TCustomListViewColProperties.GetWidthsStr: string;var  Index: Integer;begin  Result := '';  for Index := 0 to Count-1 do  begin    Result := Format('%s;%d,%d', [Result, SaveDimension(Widths[Index]), Integer(Visible[Index])]);  end;  Delete(Result, 1, 1);end;procedure TCustomListViewColProperties.BeginUpdate;begin  Columns.BeginUpdate;  Inc(FUpdating);end;procedure TCustomListViewColProperties.EndUpdate;begin  Columns.EndUpdate;  Dec(FUpdating);  if FUpdating = 0 then  begin    // call Changed() even when FChange is false    Changed;    FChanged := False;  end;end;procedure TCustomListViewColProperties.Changed;begin  if FUpdating > 0 then FChanged := True    else  if Assigned(FOnChange) then FOnChange(Self);end;procedure TCustomListViewColProperties.CheckBounds(Index: Integer);begin  if (Index < 0) or (Index >= Count) then    raise Exception.Create('Index out of bounds.');end;function TCustomListViewColProperties.GetProperties(Index: Integer): TCustomListViewColProperty;begin  Result := TCustomListViewColProperty(FProperties.Items[Index]);end;function TCustomListViewColProperties.GetIndexByOrder(Order: Integer): Integer;var  I: Integer;begin  for I := 0 to Count - 1 do  begin    if GetProperties(I).Order = Order then    begin      Result := I;      Exit;    end;  end;  raise Exception.Create('Column order out of bounds');end;function TCustomListViewColProperties.ColumnsExists: Boolean;begin  Result := FListView.HandleAllocated;  if Result and (not FCreated) and (not FListViewManaged) then    UpdateListView;end;procedure TCustomListViewColProperties.SetAlignments(Index: Integer; Value: TAlignment);begin  CheckBounds(Index);  if Alignments[Index] <> Value then  begin    GetProperties(Index).Alignment := Value;    if ColumnsExists then GetColumn(Index).Alignment := Value;    Changed;  end;end;procedure TCustomListViewColProperties.SetCaptions(Index: Integer; Value: string);begin  CheckBounds(Index);  if Captions[Index] <> Value then  begin    if ColumnsExists then GetColumn(Index).Caption := Value      else GetProperties(Index).Caption := Value;    Changed;  end;end;function TCustomListViewColProperties.GetAlignments(Index: Integer): TAlignment;begin  CheckBounds(Index);  if ColumnsExists then Result := GetColumn(Index).Alignment    else Result := GetProperties(Index).Alignment;end;function TCustomListViewColProperties.GetCaptions(Index: Integer): string;begin  CheckBounds(Index);  if ColumnsExists then Result := GetColumn(Index).Caption    else Result := GetProperties(Index).Caption;end;procedure TCustomListViewColProperties.SetOrderStr(Value: string);var  Order, Index: Integer;  Properties: TCustomListViewColProperty;  STemp: string;  Phase: Boolean;begin  BeginUpdate;  try    for Index := 0 to Count - 1 do      GetProperties(Index).Order := -1;    // First order invisible columns (not True), then visible (not not True)    Phase := True;    Order := 0;    repeat      Phase := not Phase;      STemp := Value;      while (STemp <> '') and (Order < Count) do      begin        Index := StrToInt(CutToChar(STemp, ';', True));        Properties := GetProperties(Index);        if (Properties.Visible = Phase) and           (Properties.Order < 0) { robustness }  then        begin          Properties.Order := Order;          Inc(Order);        end;      end;      // add missing columns from the same visibility class      for Index := 0 to Count - 1 do      begin        Properties := GetProperties(Index);        if (Properties.Visible = Phase) and           (Properties.Order < 0) then        begin          Properties.Order := Order;          Inc(Order);        end;      end;    until Phase;    if ColumnsExists then      UpdateListViewOrder;  finally    EndUpdate;  end;end;procedure TCustomListViewColProperties.SetParamsStr(Value: string);var  S: string;  WidthsStr: string;  OrderStr: string;  PixelsPerInch: Integer;begin  // TFileFindDialog uses / as separator of its settings  S := CutToChar(Value, '|', True);  WidthsStr := CutToChar(S, '@', True);  PixelsPerInch := LoadPixelsPerInch(S, FListView);  SetWidthsStr(WidthsStr, PixelsPerInch);  // Have to set order after visibility, otherwise we lost ordering of columns that are invisible by default,  // but visible by configuration (as they would get ordered to the front)  OrderStr := CutToChar(Value, '|', True);  SetOrderStr(OrderStr);end;procedure TCustomListViewColProperties.SetVisible(Index: Integer; Value: Boolean);var  I: Integer;  Properties: TCustomListViewColProperty;begin  CheckBounds(Index);  if Visible[Index] <> Value then  begin    Properties := GetProperties(Index);    if ColumnsExists then      UpdateOrderFromListView;    if Value then    begin      // shown column is moved to the back      for I := 0 to Count - 1 do      begin        if GetProperties(I).Order > Properties.Order then          Dec(GetProperties(I).Order);      end;      Properties.Order := Count - 1;      if ColumnsExists then        UpdateListViewOrder;      // show only after reordering column      Properties.Visible := True;      if ColumnsExists then        SetRuntimeVisible(Index, True, True);    end      else    begin      // hide before reordering column      Properties.Visible := False;      if ColumnsExists then        SetRuntimeVisible(Index, False, True);      // hidden column is moved to the front,      // unless column to the left is not hidden already      // (or unless it is first already, in which case the      // condition in the loop is never satisfied)      if (Properties.Order > 0) and         GetProperties(GetIndexByOrder(Properties.Order - 1)).Visible then      begin        for I := 0 to Count - 1 do        begin          if GetProperties(I).Order < Properties.Order then            Inc(GetProperties(I).Order);        end;        Properties.Order := 0;      end;      if ColumnsExists then        UpdateListViewOrder;    end;    Changed;    // It does not refresh itself at last one on Win7 when DoubleBuffered    if FListView.HandleAllocated then      FListView.Invalidate;  end;end;procedure TCustomListViewColProperties.SetRuntimeVisible(  Index: Integer; Value: Boolean; SaveWidth: Boolean);var  Properties: TCustomListViewColProperty;begin  // This is probably only ever called from file panels (DirViews)  // as other uses ("sychronization checklist" and "file find")  // have FListViewManaged = False and never change Visible property  // (though user can hide some columns manually in configuration storage)  with GetColumn(Index) do  begin    Properties := GetProperties(Index);    if Value then    begin      MaxWidth := Properties.MaxWidth;      MinWidth := Properties.MinWidth;      Width := Properties.Width;    end      else    begin      if SaveWidth then        Properties.Width := Width;      MaxWidth := 1;      MinWidth := 0;      Width := 0    end;  end;end;procedure TCustomListViewColProperties.SetWidths(Index: Integer; Value: Integer);var  Properties: TCustomListViewColProperty;begin  CheckBounds(Index);  Properties := GetProperties(Index);  if (Properties.MinWidth > 0) and (Value < Properties.MinWidth) then  begin    Value := Properties.MinWidth;  end    else  if (Properties.MaxWidth > 0) and (Value > Properties.MaxWidth) then  begin    Value := Properties.MaxWidth;  end;  if Widths[Index] <> Value then  begin    Properties.Width := Value;    if ColumnsExists and Visible[Index] then GetColumn(Index).Width := Value;    Changed;  end;end;function TCustomListViewColProperties.GetColumns: TListColumns;begin  Result := TListView(FListView).Columns;end;function TCustomListViewColProperties.GetColumn(Index: Integer): TListColumn;begin  Result := Columns[Index];end;function TCustomListViewColProperties.GetCount: Integer;begin  Result := FProperties.Count;end;function TCustomListViewColProperties.GetOrderStr: string;var  Index: Integer;begin  Result := '';  if ColumnsExists then    UpdateOrderFromListView;  for Index := 0 to Count - 1 do    Result := Format('%s;%d', [Result, GetIndexByOrder(Index)]);  Delete(Result, 1, 1);end;function TCustomListViewColProperties.GetParamsStr: string;begin  // WORKAROUND  // Adding an additional semicolon after the list,  // to ensure that old versions that did not expect the pixels-per-inch part,  // stop at the semicolon, otherwise they try to parse the  // "last-column-width|pixels-per-inch" as integer and throw.  // For the other instance of this hack, see GetListViewStr.  // The new pixels-per-inch part is inserted after the widths part  // as parsing of this was always robust to stop at "count" elements,  // what order part was not (due to its logic of skipping hidden columns)  Result := Format('%s;@%s|%s', [GetWidthsStr, SavePixelsPerInch(FListView), GetOrderStr]);end;function TCustomListViewColProperties.GetVisible(Index: Integer): Boolean;begin  CheckBounds(Index);  Result := GetProperties(Index).Visible;end;function TCustomListViewColProperties.GetWidths(Index: Integer): Integer;begin  CheckBounds(Index);  if ColumnsExists and Visible[Index] then  begin    Result := GetColumn(Index).Width;  end    else  begin    Result := GetProperties(Index).Width;  end;end;procedure TCustomListViewColProperties.RecreateColumns;var  Copy: TListColumns;begin  Copy := TListColumns.Create(nil);  try    Copy.Assign(Columns);    Columns.Assign(Copy);  finally    Copy.Free;  end;end;procedure TCustomListViewColProperties.CreateProperties(ACount: Integer);var  Index: Integer;  Properties: TCustomListViewColProperty;begin  for Index := 0 to ACount - 1 do  begin    Properties := TCustomListViewColProperty.Create(Index);    FProperties.Add(Properties);  end;end;procedure TCustomListViewColProperties.ListViewWndCreated;var  Index: Integer;  Properties: TCustomListViewColProperty;  Column: TListColumn;  W: Integer;begin  if FListViewManaged then  begin    if (FProperties.Count = 0) and (Columns.Count > 0) then      CreateProperties(Columns.Count);    UpdateFromListView;  end    else  begin    UpdateListView;  end;  if not FConstraintsInitialized then  begin    FConstraintsInitialized := True;    for Index := 0 to Count - 1 do    begin      Column := GetColumn(Index);      Properties := GetProperties(Index);      // Is this branching needed?      if Properties.Visible then      begin        W := Column.MaxWidth;        if W = 0 then W := DefaultListViewMaxWidth;        Properties.MaxWidth := ScaleByTextHeight(FListView, W);        W := Column.MinWidth;        if W = 0 then W := DefaultListViewMinWidth;        Properties.MinWidth := ScaleByTextHeight(FListView, W);      end        else      begin        Column.MaxWidth := ScaleByTextHeight(FListView, Column.MaxWidth);        Column.MinWidth := ScaleByTextHeight(FListView, Column.MinWidth);      end;    end;  end;  // To apply the default constraints to columns that do not have their own  UpdateListViewMaxMinWidth;end;procedure TCustomListViewColProperties.ListViewWndDestroying;begin  UpdateFromListView;end;procedure TCustomListViewColProperties.ListViewWndDestroyed;begin  if not FListViewManaged then    FCreated := False;end;procedure TCustomListViewColProperties.UpdateListViewOrder;var  Index: Integer;  Properties: TCustomListViewColProperty;  Temp: array of Integer;begin  SetLength(Temp, Count);  // Seemingly useless,  // but probably only because we swallow HDN_ENDDRAG in TCustomIEListView.WMNotify,  // what prevents VLC from actually reordering columns collection  ListView_GetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));  for Index := 0 to Count - 1 do  begin    Properties := GetProperties(Index);    Temp[Properties.Order] := Index;  end;  ListView_SetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));end;procedure TCustomListViewColProperties.UpdateListViewMaxMinWidth;var  Index: Integer;  Column: TListColumn;  Properties: TCustomListViewColProperty;begin  Assert(ColumnsExists);  for Index := 0 to Count-1 do  begin    Column := GetColumn(Index);    Properties := GetProperties(Index);    if Properties.Visible then    begin      Column.MaxWidth := Properties.MaxWidth;      if Column.Width > Column.MaxWidth then Column.Width := Column.MaxWidth;      Column.MinWidth := Properties.MinWidth;      if Column.Width < Column.MinWidth then Column.Width := Column.MinWidth;    end;  end;end;procedure TCustomListViewColProperties.UpdateListView;var  Index: Integer;  Column: TListColumn;  Properties: TCustomListViewColProperty;begin  // Only called when FListViewManaged = False  BeginUpdate;  try    for Index := 0 to Count-1 do    begin      if Index < Columns.Count then        Column := GetColumn(Index)      else        Column := Columns.Add;      Properties := GetProperties(Index);      Column.Alignment := Properties.Alignment;      Column.Caption := Properties.Caption;      SetRuntimeVisible(Index, Properties.Visible, False);    end;    UpdateListViewOrder;  finally    FCreated := True;    EndUpdate;  end;end;procedure TCustomListViewColProperties.UpdateOrderFromListView;var  Index: Integer;  Temp: array of Integer;begin  SetLength(Temp, Count);  ListView_GetColumnOrderArray(FListView.Handle, Count, PInteger(Temp));  for Index := 0 to Count - 1 do  begin    GetProperties(Temp[Index]).Order := Index;  end;end;procedure TCustomListViewColProperties.UpdateFromListView;var  Index: Integer;  Column: TListColumn;  Properties: TCustomListViewColProperty;begin  Assert(FProperties.Count = Columns.Count);  for Index := 0 to Count-1 do  begin    Column := GetColumn(Index);    Properties := GetProperties(Index);    Properties.Alignment := Column.Alignment;    Properties.Caption := Column.Caption;    if Properties.Visible then    begin      Properties.Width := Column.Width;      if Column.MaxWidth > 0 then        Properties.MaxWidth := Column.MaxWidth;      if Column.MinWidth > 0 then        Properties.MinWidth := Column.MinWidth;    end;  end;  UpdateOrderFromListView;end;end.
 |