Added: moving, dragging and copying of map selection

Added: test application for moving logic
This commit is contained in:
Mark van Renswoude 2014-07-01 09:37:25 +00:00
parent f82b8bdcf8
commit cbd10a0961
8 changed files with 726 additions and 112 deletions

View File

@ -1,57 +0,0 @@
unit Game.Map;
interface
uses
Game.Intf;
type
TGameMap = class(TInterfacedObject, IGameMap)
private
FCategory: string;
FName: string;
FDisplayName: string;
public
constructor Create(const AName: string; const ADisplayName, ACategory: string);
{ IGameMap }
function GetCategory: string;
function GetName: string;
function GetDisplayName: string;
end;
implementation
uses
System.StrUtils;
{ TGameMap }
constructor TGameMap.Create(const AName, ADisplayName, ACategory: string);
begin
inherited Create;
FName := AName;
FDisplayName := IfThen(Length(ADisplayName) > 0, ADisplayName, AName);
FCategory := ACategory;
end;
function TGameMap.GetCategory: string;
begin
Result := FCategory;
end;
function TGameMap.GetName: string;
begin
Result := FName;
end;
function TGameMap.GetDisplayName: string;
begin
Result := FDisplayName;
end;
end.

View File

@ -83,6 +83,7 @@ object MainForm: TMainForm
Margins.Right = 8
Margins.Bottom = 8
Align = alClient
DragMode = dmAutomatic
Header.AutoSizeIndex = 0
Header.Font.Charset = DEFAULT_CHARSET
Header.Font.Color = clWindowText
@ -91,9 +92,13 @@ object MainForm: TMainForm
Header.Font.Style = []
Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible]
TabOrder = 0
TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoChangeScale]
TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toWheelPanning, toEditOnClick]
TreeOptions.PaintOptions = [toHideFocusRect, toShowDropmark, toThemeAware, toUseBlendedImages]
TreeOptions.SelectionOptions = [toFullRowSelect, toMiddleClickSelect, toMultiSelect, toRightClickSelect]
OnChange = vstMapListChange
OnDragOver = vstMapListDragOver
OnDragDrop = vstMapListDragDrop
OnGetText = vstMapListGetText
Columns = <
item
@ -107,7 +112,7 @@ object MainForm: TMainForm
WideText = 'Category'
end>
end
object ToolBar1: TToolBar
object tbMapList: TToolBar
AlignWithMargins = True
Left = 8
Top = 8
@ -2264,39 +2269,8 @@ object MainForm: TMainForm
end
object alMain: TActionList
Images = glToolbar
Left = 44
Top = 276
object actGameAdd: TAction
Caption = '&Add game'
ImageIndex = 0
OnExecute = actGameAddExecute
end
object actGameRemove: TAction
Caption = '&Remove game'
Enabled = False
ImageIndex = 1
OnExecute = actGameRemoveExecute
end
object actMapAdd: TAction
Caption = '&Add map'
ImageIndex = 0
OnExecute = actMapAddExecute
end
object actMapRemove: TAction
Caption = '&Remove map'
Enabled = False
ImageIndex = 1
end
object actMapUp: TAction
Caption = 'Move &up'
Enabled = False
ImageIndex = 2
end
object actMapDown: TAction
Caption = 'Move &down'
Enabled = False
ImageIndex = 3
end
Left = 212
Top = 440
object actLaunch: TAction
Caption = '&Launch server'
end
@ -2318,4 +2292,54 @@ object MainForm: TMainForm
Action = actCopyCmdLine
end
end
object alMapList: TActionList
Images = glToolbar
Left = 356
Top = 440
object actMapAdd: TAction
Caption = '&Add map'
ImageIndex = 0
ShortCut = 16462
OnExecute = actMapAddExecute
end
object actMapRemove: TAction
Caption = '&Remove map'
Enabled = False
ImageIndex = 1
ShortCut = 16430
OnExecute = actMapRemoveExecute
end
object actMapUp: TAction
Caption = 'Move &up'
Enabled = False
ImageIndex = 2
ShortCut = 16422
OnExecute = actMapUpExecute
end
object actMapDown: TAction
Caption = 'Move &down'
Enabled = False
ImageIndex = 3
ShortCut = 16424
OnExecute = actMapDownExecute
end
end
object alGames: TActionList
Images = glToolbar
Left = 284
Top = 440
object actGameAdd: TAction
Caption = '&Add game'
ImageIndex = 0
ShortCut = 16462
OnExecute = actGameAddExecute
end
object actGameRemove: TAction
Caption = '&Remove game'
Enabled = False
ImageIndex = 1
ShortCut = 16430
OnExecute = actGameRemoveExecute
end
end
end

View File

@ -5,6 +5,7 @@ uses
System.Bindings.Expression,
System.Classes,
System.Generics.Collections,
System.Types,
Vcl.ActnList,
Vcl.ComCtrls,
Vcl.Controls,
@ -16,6 +17,7 @@ uses
Vcl.Menus,
Vcl.StdCtrls,
Vcl.ToolWin,
Winapi.ActiveX,
JvExControls,
JvExMask,
@ -33,8 +35,6 @@ uses
type
TMainForm = class(TForm)
actGameAdd: TAction;
actGameRemove: TAction;
actMapAdd: TAction;
actMapDown: TAction;
actMapRemove: TAction;
@ -105,7 +105,7 @@ type
tbMapRemove: TToolButton;
tbMapSep1: TToolButton;
tbMapUp: TToolButton;
ToolBar1: TToolBar;
tbMapList: TToolBar;
tsAbout: TTabSheet;
tsConfiguration: TTabSheet;
tsGames: TTabSheet;
@ -117,6 +117,10 @@ type
actCopyCmdLine: TAction;
actSave: TAction;
actClose: TAction;
alMapList: TActionList;
alGames: TActionList;
actGameAdd: TAction;
actGameRemove: TAction;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
@ -125,13 +129,19 @@ type
procedure llLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
procedure SpinEditChange(Sender: TObject);
procedure EditChange(Sender: TObject);
procedure actCloseExecute(Sender: TObject);
procedure actGameAddExecute(Sender: TObject);
procedure actGameRemoveExecute(Sender: TObject);
procedure vstGamesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure vstGamesFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
procedure vstMapListGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure vstMapListChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure vstMapListDragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
procedure vstMapListDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
procedure actMapAddExecute(Sender: TObject);
procedure actCloseExecute(Sender: TObject);
procedure actMapRemoveExecute(Sender: TObject);
procedure actMapUpExecute(Sender: TObject);
procedure actMapDownExecute(Sender: TObject);
private type
TBindingExpressionList = TList<TBindingExpression>;
TPageMenuMap = TDictionary<TTabSheet, TX2MenuBarItem>;
@ -142,6 +152,7 @@ type
procedure SetActiveGame(const Value: TCustomGame);
protected
procedure EnablePageActions;
procedure ActiveGameChanged;
procedure ClearUIBindings;
@ -153,6 +164,12 @@ type
procedure UpdateGameList;
procedure UpdateMapList;
function FindMapNode(AMap: TGameMap): PVirtualNode;
procedure HandleMapSelection(ANodes: TNodeArray; ATargetIndex: Integer; ACopy: Boolean);
procedure MoveMapSelection(ANodes: TNodeArray; ATargetIndex: Integer);
procedure CopyMapSelection(ANodes: TNodeArray; ATargetIndex: Integer);
property ActiveGame: TCustomGame read FActiveGame write SetActiveGame;
property PageMenuMap: TPageMenuMap read FPageMenuMap;
property UIBindings: TBindingExpressionList read FUIBindings;
@ -165,7 +182,6 @@ uses
System.Math,
System.StrUtils,
System.SysUtils,
System.Types,
Vcl.Graphics,
Vcl.GraphUtil,
Vcl.Themes,
@ -278,6 +294,17 @@ begin
end;
procedure TMainForm.EnablePageActions;
const
ActionListState: array[Boolean] of TActionListState = (asSuspended, asNormal);
begin
{ Prevent shortcuts from triggering on invisible pages }
alMapList.State := ActionListState[pcMain.ActivePage = tsMapList];
alGames.State := ActionListState[pcMain.ActivePage = tsGames];
end;
procedure TMainForm.ActiveGameChanged;
begin
ClearUIBindings;
@ -377,14 +404,129 @@ end;
procedure TMainForm.UpdateMapList;
var
gameMapList: IGameMapList;
begin
if Assigned(ActiveGame) then
vstMapList.RootNodeCount := (ActiveGame as IGameMapList).GetMapList.Count
if Supports(ActiveGame, IGameMapList, gameMapList) then
vstMapList.RootNodeCount := gameMapList.GetMapList.Count
else
vstMapList.Clear;
end;
function TMainForm.FindMapNode(AMap: TGameMap): PVirtualNode;
var
gameMapList: IGameMapList;
begin
Result := nil;
if not Supports(ActiveGame, IGameMapList, gameMapList) then
exit;
Result := vstMapList.IterateSubtree(nil,
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
begin
Abort := (gameMapList.GetMapList[Node^.Index] = AMap);
end,
nil);
end;
procedure TMainForm.HandleMapSelection(ANodes: TNodeArray; ATargetIndex: Integer; ACopy: Boolean);
var
gameMapList: IGameMapList;
node: PVirtualNode;
targetIndex: Integer;
sourceShift: Integer;
sourceIndex: Integer;
newIndex: Integer;
newNode: PVirtualNode;
selectedMaps: TList<TGameMap>;
map: TGameMap;
begin
if (ATargetIndex < 0) or (ATargetIndex > vstMapList.RootNodeCount) then
exit;
if not Supports(ActiveGame, IGameMapList, gameMapList) then
exit;
targetIndex := ATargetIndex;
sourceShift := 0;
vstMapList.BeginUpdate;
try
selectedMaps := TList<TGameMap>.Create;
try
for node in ANodes do
begin
sourceIndex := node^.Index;
if ACopy then
begin
{ Copy map nodes }
Inc(sourceIndex, sourceShift);
map := TGameMap.Create(gameMapList.GetMapList[sourceIndex]);
gameMapList.GetMapList.Insert(targetIndex, map);
selectedMaps.Add(map);
Inc(targetIndex);
if sourceIndex > ATargetIndex then
Inc(sourceShift);
end else
begin
{ Move map nodes }
if sourceIndex < ATargetIndex then
Inc(sourceIndex, sourceShift);
if targetIndex > sourceIndex then
begin
newIndex := Pred(targetIndex);
Dec(sourceShift);
end else
begin
newIndex := targetIndex;
Inc(targetIndex);
end;
selectedMaps.Add(gameMapList.GetMapList[sourceIndex]);
gameMapList.GetMapList.Move(sourceIndex, newIndex);
end;
end;
vstMapList.ClearSelection;
vstMapList.IterateSubtree(nil,
procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean)
begin
if selectedMaps.Contains(gameMapList.GetMapList[Node^.Index]) then
Sender.Selected[Node] := True;
end,
nil);
finally
FreeAndNil(selectedMaps);
end;
finally
vstMapList.EndUpdate;
end;
end;
procedure TMainForm.MoveMapSelection(ANodes: TNodeArray; ATargetIndex: Integer);
begin
HandleMapSelection(ANodes, ATargetIndex, False);
end;
procedure TMainForm.CopyMapSelection(ANodes: TNodeArray; ATargetIndex: Integer);
begin
HandleMapSelection(ANodes, ATargetIndex, True);
end;
procedure TMainForm.SetActiveGame(const Value: TCustomGame);
begin
if Value <> FActiveGame then
@ -404,6 +546,7 @@ end;
procedure TMainForm.mbMenuSelectedChanged(Sender: TObject; Item: TX2CustomMenuBarItem);
begin
pcMain.ActivePageIndex := Item.Tag;
EnablePageActions;
end;
@ -425,6 +568,12 @@ begin
end;
procedure TMainForm.actCloseExecute(Sender: TObject);
begin
Close;
end;
procedure TMainForm.actGameAddExecute(Sender: TObject);
var
game: TCustomGame;
@ -505,13 +654,14 @@ end;
procedure TMainForm.vstMapListGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
gameMapList: IGameMapList;
map: TGameMap;
begin
if not Assigned(ActiveGame) then
if not Supports(ActiveGame, IGameMapList, gameMapList) then
exit;
map := (ActiveGame as IGameMapList).GetMapList[Node^.Index];
map := gameMapList.GetMapList[Node^.Index];
case Column of
MapColumnName:
@ -523,28 +673,152 @@ begin
end;
procedure TMainForm.actMapAddExecute(Sender: TObject);
procedure TMainForm.vstMapListChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
mapList: IGameMapList;
map: TGameMap;
hasSelection: Boolean;
begin
mapList := ActiveGame as IGameMapList;
hasSelection := (Sender.SelectedCount > 0);
if TMapForm.Insert(Self, mapList, map) then
actMapRemove.Enabled := hasSelection;
actMapUp.Enabled := hasSelection;
actMapDown.Enabled := hasSelection;
end;
procedure TMainForm.vstMapListDragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint;
Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
begin
Accept := (Source = Sender);
end;
procedure TMainForm.vstMapListDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray;
Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
var
sourceTree: TBaseVirtualTree;
selectedNodes: TNodeArray;
targetIndex: Integer;
lastNode: PVirtualNode;
begin
sourceTree := (Source as TBaseVirtualTree);
targetIndex := 0;
case Mode of
dmNowhere:
begin
lastNode := Sender.GetLast;
if Assigned(lastNode) then
targetIndex := Succ(lastNode^.Index);
end;
dmAbove,
dmOnNode:
targetIndex := Sender.DropTargetNode^.Index;
dmBelow:
targetIndex := Succ(Sender.DropTargetNode^.Index);
end;
selectedNodes := sourceTree.GetSortedSelection(True);
if Effect = DROPEFFECT_COPY then
CopyMapSelection(selectedNodes, targetIndex)
else
MoveMapSelection(selectedNodes, targetIndex);
end;
procedure TMainForm.actMapAddExecute(Sender: TObject);
var
gameMapList: IGameMapList;
map: TGameMap;
node: PVirtualNode;
begin
if not Supports(ActiveGame, IGameMapList, gameMapList) then
exit;
if TMapForm.Insert(Self, gameMapList, map) then
begin
mapList.GetMapList.Add(map);
gameMapList.GetMapList.Add(map);
UpdateMapList;
node := FindMapNode(map);
if Assigned(node) then
begin
vstMapList.ClearSelection;
vstMapList.FocusedNode := node;
vstMapList.Selected[node] := True;
end;
end;
end;
procedure TMainForm.actCloseExecute(Sender: TObject);
procedure TMainForm.actMapRemoveExecute(Sender: TObject);
var
gameMapList: IGameMapList;
selectedNodes: TNodeArray;
nodeIndex: Integer;
begin
Close;
if not Supports(ActiveGame, IGameMapList, gameMapList) then
exit;
if vstMapList.SelectedCount = 0 then
exit;
if MessageBox(Self.Handle, 'Do you want to remove the selected map(s)?', 'Remove', MB_YESNO or MB_ICONQUESTION) = ID_YES then
begin
vstMapList.BeginUpdate;
try
selectedNodes := vstMapList.GetSortedSelection(True);
for nodeIndex := High(selectedNodes) downto Low(selectedNodes) do
gameMapList.GetMapList.Delete(selectedNodes[nodeIndex]^.Index);
UpdateMapList;
finally
vstMapList.ClearSelection;
vstMapList.EndUpdate;
end;
end;
end;
procedure TMainForm.actMapUpExecute(Sender: TObject);
var
selectedNodes: TNodeArray;
targetIndex: Integer;
begin
selectedNodes := vstMapList.GetSortedSelection(True);
if Length(selectedNodes) = 0 then
exit;
targetIndex := Pred(selectedNodes[0]^.Index);
if targetIndex < 0 then
targetIndex := 0;
MoveMapSelection(selectedNodes, targetIndex);
end;
procedure TMainForm.actMapDownExecute(Sender: TObject);
var
selectedNodes: TNodeArray;
targetIndex: Integer;
begin
selectedNodes := vstMapList.GetSortedSelection(True);
if Length(selectedNodes) = 0 then
exit;
targetIndex := Succ(selectedNodes[High(selectedNodes)]^.Index) + 1;
MoveMapSelection(selectedNodes, targetIndex);
end;
{ TINIHintWindow }
function TINIHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect;

View File

@ -350,10 +350,8 @@ begin
vstMap.FocusedNode := node;
if Assigned(node) then
begin
vstMap.Selected[node] := True;
vstMap.ScrollIntoView(node, True);
end else
vstMap.Selected[node] := True
else
vstMap.ClearSelection;
end;

View File

@ -0,0 +1,11 @@
program MultiselectMoveTest;
uses
GUITestRunner,
MultiselectMoveTestCase in 'MultiselectMoveTestCase.pas';
{$R *.res}
begin
RunRegisteredTests;
end.

View File

@ -0,0 +1,139 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{EFFF3F42-880C-43A5-BDB6-ED22365C2CEB}</ProjectGuid>
<ProjectVersion>13.4</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>MultiselectMoveTest.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Console</AppType>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<DCC_UsePackage>fmx;IndySystem;DBXInterBaseDriver;DataSnapClient;DataSnapCommon;DataSnapServer;DataSnapProviderClient;DbxCommonDriver;dbxcds;DBXOracleDriver;CustomIPTransport;dsnap;fmxase;IndyCore;inetdbxpress;IPIndyImpl;bindcompfmx;rtl;dbrtl;DbxClientDriver;bindcomp;inetdb;xmlrtl;ibxpress;IndyProtocols;DBXMySQLDriver;soaprtl;bindengine;DBXInformixDriver;DBXFirebirdDriver;inet;fmxobj;DBXSybaseASADriver;fmxdae;dbexpress;DataSnapIndy10ServerTransport;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<DCC_UsePackage>cxLibraryRS16;dxLayoutControlRS16;dxCoreRS16;cxExportRS16;dxBarRS16;TeeDB;vclib;dxPSCoreRS16;cxPageControlRS16;DBXSybaseASEDriver;vclimg;dxComnRS16;vcldb;vcldsnap;X2CLGL;DBXDb2Driver;vcl;DBXMSSQLDriver;cxDataRS16;webdsnap;X2CLMB;dxPSTeeChartRS16;adortl;dxPSdxLCLnkRS16;dxorgcRS16;dxWizardControlRS16;Tee;DBXOdbcDriver;dxmdsRS16;cxGridRS16;cxEditorsRS16;TeeUI;vclactnband;dxServerModeRS16;bindcompvcl;cxPivotGridRS16;vclie;cxSchedulerRS16;vcltouch;websnap;VclSmp;DataSnapConnectors;dsnapcon;dxThemeRS16;vclx;VirtualTreesR;dxGDIPlusRS16;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>JvGlobus;JvMM;cxLibraryRS16;dxLayoutControlRS16;JvManagedThreads;dxCoreRS16;cxExportRS16;dxBarRS16;JvDlgs;JvCrypt;TeeDB;rbDIDE1516;vclib;inetdbbde;dxPSCoreRS16;rbTCUI1516;JvNet;cxPageControlRS16;JvDotNetCtrls;DBXSybaseASEDriver;vclimg;rbTC1516;fmi;rbDAD1516;dxComnRS16;JvXPCtrls;rbRCL1516;vcldb;vcldsnap;X2CLGL;rbIBE1516;DBXDb2Driver;dclRBBDE1516;rbDB1516;JvCore;dclRBIBE1516;dclRBADO1516;vcl;CloudService;DBXMSSQLDriver;CodeSiteExpressPkg;FmxTeeUI;cxDataRS16;rbRAP1516;JvAppFrm;JvDB;JvRuntimeDesign;webdsnap;X2CLMB;OmniThreadLibraryRuntimeXE2;JclDeveloperTools;rbRIDE1516;dxPSTeeChartRS16;adortl;dxPSdxLCLnkRS16;JvDocking;JvWizards;rbADO1516;madBasic_;dxorgcRS16;JvHMI;dxWizardControlRS16;JvBands;vcldbx;rbDBDE1516;dclRBE1516;rbIDE1516;JvPluginSystem;Tee;rbUSERDesign1516;DBXOdbcDriver;JvCmp;madDisAsm_;JvSystem;svnui;JvTimeFramework;JvControls;dxmdsRS16;cxGridRS16;cxEditorsRS16;FMXTee;TeeUI;vclactnband;dxServerModeRS16;bindcompvcl;cxPivotGridRS16;JvStdCtrls;Jcl;vclie;JvCustom;JvJans;cxSchedulerRS16;madExcept_;JvPageComps;vcltouch;JvPrintPreview;rbBDE1516;websnap;rbUSER1516;dclRBDBE1516;VclSmp;DataSnapConnectors;dsnapcon;dxThemeRS16;JclVcl;JvPascalInterpreter;vclx;rbCIDE1516;svn;rbDBE1516;bdertl;VirtualTreesR;dxGDIPlusRS16;JvBDE;$(DCC_UsePackage)</DCC_UsePackage>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
<DCC_RemoteDebug>true</DCC_RemoteDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>false</DCC_DebugInformation>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="MultiselectMoveTestCase.pas"/>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType/>
<BorlandProject>
<Delphi.Personality>
<VersionInfo>
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
<VersionInfo Name="MajorVer">1</VersionInfo>
<VersionInfo Name="MinorVer">0</VersionInfo>
<VersionInfo Name="Release">0</VersionInfo>
<VersionInfo Name="Build">0</VersionInfo>
<VersionInfo Name="Debug">False</VersionInfo>
<VersionInfo Name="PreRelease">False</VersionInfo>
<VersionInfo Name="Special">False</VersionInfo>
<VersionInfo Name="Private">False</VersionInfo>
<VersionInfo Name="DLL">False</VersionInfo>
<VersionInfo Name="Locale">1043</VersionInfo>
<VersionInfo Name="CodePage">1252</VersionInfo>
</VersionInfo>
<VersionInfoKeys>
<VersionInfoKeys Name="CompanyName"/>
<VersionInfoKeys Name="FileDescription"/>
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
<VersionInfoKeys Name="InternalName"/>
<VersionInfoKeys Name="LegalCopyright"/>
<VersionInfoKeys Name="LegalTrademarks"/>
<VersionInfoKeys Name="OriginalFilename"/>
<VersionInfoKeys Name="ProductName"/>
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
<VersionInfoKeys Name="Comments"/>
</VersionInfoKeys>
<Source>
<Source Name="MainSource">MultiselectMoveTest.dpr</Source>
</Source>
</Delphi.Personality>
<Deployment/>
<Platforms>
<Platform value="Win64">False</Platform>
<Platform value="Win32">True</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')" Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/>
<Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/>
</Project>

Binary file not shown.

View File

@ -0,0 +1,225 @@
unit MultiselectMoveTestCase;
interface
uses
System.Generics.Collections,
TestFramework;
type
TMultiselectMoveTest = class(TTestCase)
private
FList: TList<Integer>;
protected
procedure SetUp; override;
procedure TearDown; override;
procedure CheckList(AExpected: array of Integer);
procedure Move(ASelection: array of Integer; ATargetIndex: Integer);
property List: TList<Integer> read FList;
published
procedure TestSetUp;
procedure TestSingleDown;
procedure TestSingleUp;
procedure TestSingleMin;
procedure TestSingleMax;
procedure TestMultipleDown;
procedure TestMultipleUp;
procedure TestMultipleMin;
procedure TestMultipleMax;
procedure TestGapDown;
procedure TestGapUp;
procedure TestGapMiddle;
procedure TestGapMin;
procedure TestGapMax;
end;
implementation
uses
System.SysUtils;
{ TMultiselectMoveTest }
procedure TMultiselectMoveTest.SetUp;
var
value: Integer;
begin
inherited SetUp;
FList := TList<Integer>.Create;
for value := 0 to 9 do
FList.Add(value);
end;
procedure TMultiselectMoveTest.TearDown;
begin
FreeAndNil(FList);
inherited TearDown;
end;
procedure TMultiselectMoveTest.CheckList(AExpected: array of Integer);
var
listValues: TStringBuilder;
expectedValues: TStringBuilder;
value: Integer;
begin
listValues := nil;
expectedValues := nil;
try
listValues := TStringBuilder.Create;
for value in List do
listValues.Append(value).Append(',');
expectedValues := TStringBuilder.Create;
for value in AExpected do
expectedValues.Append(value).Append(',');
CheckEquals(expectedValues.ToString, listValues.ToString);
finally
FreeAndNil(expectedValues);
FreeAndNil(listValues);
end;
end;
procedure TMultiselectMoveTest.Move(ASelection: array of Integer; ATargetIndex: Integer);
var
targetIndex: Integer;
sourceShift: Integer;
index: Integer;
sourceIndex: Integer;
begin
targetIndex := ATargetIndex;
sourceShift := 0;
for index in ASelection do
begin
sourceIndex := index;
if index < ATargetIndex then
Inc(sourceIndex, sourceShift);
if targetIndex > sourceIndex then
begin
List.Move(sourceIndex, Pred(targetIndex));
Dec(sourceShift);
end else if targetIndex < sourceIndex then
begin
List.Move(sourceIndex, targetIndex);
Inc(targetIndex);
end;
end;
end;
procedure TMultiselectMoveTest.TestSetUp;
begin
CheckList([0,1,2,3,4,5,6,7,8,9]);
end;
procedure TMultiselectMoveTest.TestSingleDown;
begin
Move([1], 5);
CheckList([0,2,3,4,1,5,6,7,8,9]);
end;
procedure TMultiselectMoveTest.TestSingleMin;
begin
Move([5], 0);
CheckList([5,0,1,2,3,4,6,7,8,9]);
end;
procedure TMultiselectMoveTest.TestSingleMax;
begin
Move([5], 10);
CheckList([0,1,2,3,4,6,7,8,9,5]);
end;
procedure TMultiselectMoveTest.TestSingleUp;
begin
Move([8], 5);
CheckList([0,1,2,3,4,8,5,6,7,9]);
end;
procedure TMultiselectMoveTest.TestMultipleDown;
begin
Move([1,2], 5);
CheckList([0,3,4,1,2,5,6,7,8,9]);
end;
procedure TMultiselectMoveTest.TestMultipleUp;
begin
Move([5,6], 1);
CheckList([0,5,6,1,2,3,4,7,8,9]);
end;
procedure TMultiselectMoveTest.TestMultipleMin;
begin
Move([5,6,7], 0);
CheckList([5,6,7,0,1,2,3,4,8,9]);
end;
procedure TMultiselectMoveTest.TestMultipleMax;
begin
Move([3,4,5], 10);
CheckList([0,1,2,6,7,8,9,3,4,5]);
end;
procedure TMultiselectMoveTest.TestGapDown;
begin
Move([3,5], 6);
CheckList([0,1,2,4,3,5,6,7,8,9]);
end;
procedure TMultiselectMoveTest.TestGapUp;
begin
Move([7,9], 3);
CheckList([0,1,2,7,9,3,4,5,6,8]);
end;
procedure TMultiselectMoveTest.TestGapMiddle;
begin
Move([3,8], 5);
CheckList([0,1,2,4,3,8,5,6,7,9]);
end;
procedure TMultiselectMoveTest.TestGapMin;
begin
Move([3,5,8], 0);
CheckList([3,5,8,0,1,2,4,6,7,9]);
end;
procedure TMultiselectMoveTest.TestGapMax;
begin
Move([4,6,9], 10);
CheckList([0,1,2,3,5,7,8,4,6,9]);
end;
initialization
RegisterTest(TMultiselectMoveTest.Suite);
end.