lazarus/components/lazcontrols/smallorderedseteditor.pas

333 lines
9.5 KiB
ObjectPascal

{ TSmallOrderedSetEditDlg
Copyright (C) 2017 Lazarus team
This library is free software; you can redistribute it and/or modify it
under the same terms as the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
Abstract:
Dialog to edit a set of items (string) and able to order them.
}
unit SmallOrderedSetEditor;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Types, math,
Forms, Controls, Graphics, Dialogs, StdCtrls,
ButtonPanel, ComCtrls, Buttons, LCLType, Themes,
LazLoggerBase, LazUTF8;
type
TSmOrdSetEditOption = (
oseoHideUpDown,
oseoErrorDuplicateItems, // default: ignore and skip
oseoErrorDuplicateAvailable, // default: ignore and skip
oseoErrorItemsContainNotAvailable // default: merge Items into AvailableItems
);
TSmOrdSetEditOptions = set of TSmOrdSetEditOption;
{ TSmallOrderedSetEditDlg }
TSmallOrderedSetEditDlg = class(TForm)
ButtonPanel1: TButtonPanel;
HeaderLabel: TLabel;
ImageList1: TImageList;
ItemsTreeView: TTreeView;
MoveDownBitBtn: TBitBtn;
MoveUpBitBtn: TBitBtn;
procedure ItemsTreeViewAdvancedCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; {%H-}State: TCustomDrawState; Stage: TCustomDrawStage;
var PaintImages, {%H-}DefaultDraw: Boolean);
procedure ItemsTreeViewMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
{%H-}Shift: TShiftState; X, Y: Integer);
procedure ItemsTreeViewSelectionChanged(Sender: TObject);
procedure MoveDownBitBtnClick(Sender: TObject);
procedure MoveUpBitBtnClick(Sender: TObject);
private
FAvailableItems: TStrings;
FItems: TStrings;
FOptions: TSmOrdSetEditOptions;
function GetHeaderCaption: TTranslateString;
procedure SetAvailableItems(const AValue: TStrings);
procedure SetHeaderCaption(const AValue: TTranslateString);
procedure SetItems(const AValue: TStrings);
function SetList(List, NewList: TStrings; ErrorOnDuplicate: boolean): boolean;
procedure UpdateButtonState;
protected
procedure SetOptions(const AValue: TSmOrdSetEditOptions); virtual;
//function IndexOf(List: TStrings; Value: string): integer; virtual;
procedure UpdateShowing; override;
procedure ToggleNode(Node: TTreeNode); virtual;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure Init; virtual;
property Options: TSmOrdSetEditOptions read FOptions write SetOptions;
property HeaderCaption: TTranslateString read GetHeaderCaption write SetHeaderCaption;
property Items: TStrings read FItems write SetItems;
property AvailableItems: TStrings read FAvailableItems write SetAvailableItems;
end;
function CreateOrderedSetEditor(Items, AvailableItems: TStrings): TSmallOrderedSetEditDlg;
function ShowOrderedSetEditor(aCaption: string; Items, AvailableItems: TStrings): TModalResult;
implementation
function CreateOrderedSetEditor(Items, AvailableItems: TStrings): TSmallOrderedSetEditDlg;
begin
Result:=TSmallOrderedSetEditDlg.Create(nil);
Result.Items:=Items;
Result.AvailableItems:=AvailableItems;
Result.Init;
end;
function ShowOrderedSetEditor(aCaption: string; Items, AvailableItems: TStrings
): TModalResult;
var
Dlg: TSmallOrderedSetEditDlg;
begin
Dlg:=CreateOrderedSetEditor(Items,AvailableItems);
try
Dlg.Caption:=aCaption;
Result:=Dlg.ShowModal;
if Result=mrOK then
Items.Assign(Dlg.Items);
finally
Dlg.Free;
end;
end;
{$R *.lfm}
{ TSmallOrderedSetEditDlg }
procedure TSmallOrderedSetEditDlg.ItemsTreeViewAdvancedCustomDrawItem(
Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
var
Selected: Boolean;
R: TRect;
Details: TThemedElementDetails;
begin
Selected:=Node.ImageIndex>0;
if Stage=cdPrePaint then
PaintImages:=false
else if Stage=cdPostPaint then
begin
R:=Node.DisplayRect(false);
R.Left := Node.DisplayIconLeft;
if Selected then
Details := ThemeServices.GetElementDetails(tbCheckBoxCheckedNormal)
else
Details := ThemeServices.GetElementDetails(tbCheckBoxUncheckedNormal);
R.Right:=R.Left+ThemeServices.GetDetailSizeForPPI(Details, PixelsPerInch).cx;
ThemeServices.DrawElement(ItemsTreeView.Canvas.Handle, Details, R, nil);
end;
end;
procedure TSmallOrderedSetEditDlg.ItemsTreeViewMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Node: TTreeNode;
begin
Node:=ItemsTreeView.GetNodeAt(X,Y);
if Node=nil then exit;
ToggleNode(Node);
end;
procedure TSmallOrderedSetEditDlg.ItemsTreeViewSelectionChanged(Sender: TObject);
begin
UpdateButtonState;
end;
procedure TSmallOrderedSetEditDlg.MoveDownBitBtnClick(Sender: TObject);
var
SelNode: TTreeNode;
begin
SelNode:=ItemsTreeView.Selected;
if (SelNode=nil) or (SelNode.Index>=ItemsTreeView.Items.TopLvlCount-1) then exit;
SelNode.Index:=SelNode.Index+1;
UpdateButtonState;
end;
procedure TSmallOrderedSetEditDlg.MoveUpBitBtnClick(Sender: TObject);
var
SelNode: TTreeNode;
begin
SelNode:=ItemsTreeView.Selected;
if (SelNode=nil) or (SelNode.Index<1) then exit;
SelNode.Index:=SelNode.Index-1;
UpdateButtonState;
end;
function TSmallOrderedSetEditDlg.GetHeaderCaption: TTranslateString;
begin
Result:=HeaderLabel.Caption;
end;
procedure TSmallOrderedSetEditDlg.SetAvailableItems(const AValue: TStrings);
begin
SetList(FAvailableItems,AValue,oseoErrorDuplicateAvailable in Options);
end;
procedure TSmallOrderedSetEditDlg.SetHeaderCaption(const AValue: TTranslateString);
begin
if HeaderCaption=AValue then Exit;
HeaderLabel.Caption:=AValue;
HeaderLabel.Visible:=HeaderLabel.Caption<>'';
end;
procedure TSmallOrderedSetEditDlg.SetItems(const AValue: TStrings);
begin
SetList(FItems,AValue,oseoErrorDuplicateItems in Options);
end;
procedure TSmallOrderedSetEditDlg.UpdateButtonState;
var
SelNode: TTreeNode;
begin
SelNode:=ItemsTreeView.Selected;
MoveUpBitBtn.Enabled:=(SelNode<>nil) and (SelNode.Index>0);
MoveDownBitBtn.Enabled:=(SelNode<>nil) and (SelNode.Index<ItemsTreeView.Items.TopLvlCount-1);
end;
procedure TSmallOrderedSetEditDlg.SetOptions(const AValue: TSmOrdSetEditOptions);
begin
if FOptions=AValue then Exit;
FOptions:=AValue;
MoveUpBitBtn.Visible:=not (oseoHideUpDown in Options);
MoveDownBitBtn.Visible:=not (oseoHideUpDown in Options);
end;
{
function TSmallOrderedSetEditDlg.IndexOf(List: TStrings; Value: string): integer;
begin
Result:=List.IndexOf(Value);
end;
}
function TSmallOrderedSetEditDlg.SetList(List, NewList: TStrings;
ErrorOnDuplicate: boolean): boolean;
var
CleanList: TStringListUTF8Fast;
i: Integer;
s: String;
begin
CleanList:=TStringListUTF8Fast.Create;
try
for i:=0 to NewList.Count-1 do
begin
s:=NewList[i];
if CleanList.IndexOf(s)>=0 then
begin
if ErrorOnDuplicate then
raise EListError.Create(DbgSName(Self)+': duplicate item '+IntToStr(i)+' "'+s+'"');
continue;
end;
CleanList.Add(s);
end;
if List.Equals(CleanList) then exit(false);
Result:=true;
List.Assign(CleanList);
finally
CleanList.Free;
end;
end;
procedure TSmallOrderedSetEditDlg.UpdateShowing;
var
CheckedDetails, UnCheckedDetails: TThemedElementDetails;
CheckedSize, UnCheckedSize: TSize;
Bmp: TBitmap;
begin
inherited UpdateShowing;
if Visible and (ImageList1.Count=0) then begin
CheckedDetails := ThemeServices.GetElementDetails(tbCheckBoxCheckedNormal);
UnCheckedDetails := ThemeServices.GetElementDetails(tbCheckBoxCheckedNormal);
CheckedSize:=ThemeServices.GetDetailSizeForPPI(CheckedDetails, PixelsPerInch);
UnCheckedSize:=ThemeServices.GetDetailSizeForPPI(UnCheckedDetails, PixelsPerInch);
ImageList1.Width:=Max(CheckedSize.cx,UnCheckedSize.cx);
ImageList1.Height:=Max(CheckedSize.cy,UnCheckedSize.cy);
Bmp:=TBitmap.Create;
Bmp.SetSize(ImageList1.Width,ImageList1.Height);
ImageList1.Add(Bmp,nil);
ImageList1.Add(Bmp,nil);
Bmp.Free;
end;
end;
procedure TSmallOrderedSetEditDlg.ToggleNode(Node: TTreeNode);
var
i, j: Integer;
begin
Node.ImageIndex:=1-Node.ImageIndex;
Node.SelectedIndex:=Node.ImageIndex;
if Node.ImageIndex=0 then
begin
i:=Items.IndexOf(Node.Text);
Items.Delete(i);
end else begin
j:=0;
for i:=0 to Node.Index-1 do
begin
if ItemsTreeView.Items[i].ImageIndex>0 then
inc(j);
end;
Items.Insert(j,Node.Text);
end;
end;
constructor TSmallOrderedSetEditDlg.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FItems:=TStringList.Create;
FAvailableItems:=TStringList.Create;
end;
destructor TSmallOrderedSetEditDlg.Destroy;
begin
FreeAndNil(FItems);
FreeAndNil(FAvailableItems);
inherited Destroy;
end;
procedure TSmallOrderedSetEditDlg.Init;
var
i: Integer;
s: String;
Node: TTreeNode;
begin
for i:=FItems.Count-1 downto 0 do
begin
s:=FItems[i];
if AvailableItems.IndexOf(s)<0 then
begin
if oseoErrorItemsContainNotAvailable in Options then
raise EListError.Create(DbgSName(Self)+': item '+IntToStr(i)+' "'+s+'" is not in AvailableItems');
FAvailableItems.Insert(0,s);
end;
end;
ItemsTreeView.BeginUpdate;
ItemsTreeView.Items.Clear;
for i:=0 to AvailableItems.Count-1 do
begin
s:=AvailableItems[i];
Node:=ItemsTreeView.Items.Add(nil,s);
if Items.IndexOf(s)>=0 then
Node.ImageIndex:=1
else
Node.ImageIndex:=0;
Node.SelectedIndex:=Node.ImageIndex;
end;
ItemsTreeView.EndUpdate;
UpdateButtonState;
end;
end.