Skip to content

Commit

Permalink
Move multirow selection object to a dedicated unit
Browse files Browse the repository at this point in the history
  • Loading branch information
varianus committed Oct 8, 2024
1 parent 568b4c1 commit 0d9887e
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 232 deletions.
249 changes: 249 additions & 0 deletions src/multirowselection.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
unit MultiRowSelection;

{$mode ObjFPC}{$H+}

interface

type
TMovingSelection = (msNone, msUp, msDown);

TRowsSelection = class(TObject)

private
type
TDWordArray = array [0..$FFFFFF] of Dword;
PDWordArray = ^TDWordArray;
private
fArray: PDWordArray;
fDwordSize: integer;
fsize: integer;
function GetSelected(Index: integer): boolean;
procedure SetSelected(Index: integer; AValue: boolean);
procedure SetSize(Size: integer);
public
constructor Create;
procedure ClearAll;
procedure SelectAll;
function FirstSelected: integer;
function LastSelected: integer;
function NextSelected(index: integer = -1): integer;
function PreviousSelected(index: integer = -1): integer;
function isMultiselection: boolean;
function IsContiguous: boolean;
procedure SelectRange(var Anchor: integer; OldRow, NewRow: integer);
property Size: integer read FSize write SetSize;
property Selected[Index: integer]: boolean read GetSelected write SetSelected; default;

end;

implementation

uses Math;

{ TRowsSelection }

function TRowsSelection.GetSelected(Index: integer): boolean;
var
DWordIndex, BitIndex: integer;
begin
if fDwordSize < 1 then
begin
Result := False;
exit;
end;
DivMod(Index, 32, DWordIndex, BitIndex);
Result := (fArray^[DWordIndex] and DWORD(1 shl (BitIndex))) <> 0;
// DebugLn('Get ', inttostr(fArray^[DWordIndex]), ' ', BoolToStr(Result, true), ' ', IntToStr(index) );
end;

procedure TRowsSelection.SetSelected(Index: integer; AValue: boolean);
var
DWordIndex, BitIndex: integer;
begin
if fDwordSize < 1 then
exit;

if (Index > size - 1) or (Index < 0) then
exit;

DivMod(Index, 32, DWordIndex, BitIndex);
if AValue then
fArray^[DWordIndex] := fArray^[DWordIndex] or DWORD(1 shl (BitIndex))
else
fArray^[DWordIndex] := fArray^[DWordIndex] and not DWORD(1 shl (BitIndex));
// DebugLn('Set ', inttostr(fArray^[DWordIndex]), ' ', BoolToStr(AValue, true), ' ', IntToStr(index) );
end;

constructor TRowsSelection.Create;
begin
fDwordSize := -1;
fArray := nil;
end;

procedure TRowsSelection.ClearAll;
var
i: integer;
begin
for i := 0 to fDwordSize - 1 do
fArray^[i] := DWord($0);
end;

procedure TRowsSelection.SelectAll;
var
i: integer;
begin
for i := 0 to fDwordSize - 2 do
fArray^[i] := DWord(not $0);

if fDwordSize > 0 then
// Clean up unused bits so LastSelected work give right result
fArray^[fDwordSize - 1] := not ($ffffffff shl (size mod 32));

end;

procedure TRowsSelection.SetSize(Size: integer);
begin
fDwordSize := ceil(Size / 32);
ReAllocMem(fArray, fDwordSize * SizeOf(DWORD));
fsize := Size;
ClearAll;
end;

function TRowsSelection.FirstSelected: integer;
const
fSkippableValue = $00000000;
var
DWordIndex, BitIndex: integer;
tmpDWord: DWORD;
begin
Result := -1;
BitIndex := 0;
for DWordIndex := 0 to fDwordSize - 1 do
begin
if fArray^[DWordIndex] = fSkippableValue then
Continue;
tmpDWord := fArray^[DWordIndex];
BitIndex := 0;
while (tmpDWord and 1) = 0 do
begin
tmpDWord := tmpDWord shr 1;
Inc(BitIndex);
end;
Result := DWordIndex * 32 + BitIndex;
exit;
end;
end;

function TRowsSelection.LastSelected: integer;
const
fSkippableValue = $00000000;
var
DWordIndex, BitIndex: integer;
tmpDWord: DWORD;
begin
Result := -1;
BitIndex := 31;
for DWordIndex := fDwordSize - 1 downto 0 do
begin
if fArray^[DWordIndex] = fSkippableValue then
Continue;
tmpDWord := fArray^[DWordIndex];
BitIndex := 31;
while (tmpDWord and $80000000) = 0 do
begin
tmpDWord := tmpDWord shl 1;
Dec(BitIndex);
end;
Result := DWordIndex * 32 + BitIndex;
exit;
end;

end;

function TRowsSelection.NextSelected(index: integer = -1): integer;
var
i: integer;
begin
Result := -1;
if index = -1 then
begin
Result := FirstSelected;
exit;
end;

for i := (index + 1) to fsize - 1 do
if GetSelected(i) then
begin
Result := i;
exit;
end;
end;

function TRowsSelection.PreviousSelected(index: integer = -1): integer;
var
i: integer;
begin
Result := -1;
if index = -1 then
begin
Result := LastSelected;
exit;
end;

for i := index - 1 downto 0 do
if GetSelected(i) then
begin
Result := i;
exit;
end;
end;

function TRowsSelection.isMultiselection: boolean;
var
idx: integer;
begin
idx := FirstSelected;
Result := not (NextSelected(idx) = -1);

end;

function TRowsSelection.IsContiguous: boolean;
var
idx: integer;
begin
Result := True;
for idx := FirstSelected to LastSelected do
begin
Result := Result and Selected[idx];
if not Result then exit;
end;

end;

procedure TRowsSelection.SelectRange(var Anchor: integer; OldRow, NewRow: integer);
var
dir: integer;
sel: boolean;
begin
if OldRow = NewRow then
exit;
if Anchor = -1 then
Anchor := OldRow;
dir := Sign(NewRow - OldRow);
if Sign(Anchor - OldRow) <> Sign(Anchor - NewRow) then
while OldRow <> Anchor do
begin
SetSelected(OldRow, False);
Inc(OldRow, dir);
end;
sel := Abs(Anchor - OldRow) < Abs(Anchor - NewRow);
while OldRow <> NewRow do
begin
SetSelected(OldRow, sel);
Inc(OldRow, dir);
end;
SetSelected(NewRow, True);
end;


end.
5 changes: 3 additions & 2 deletions src/ovotag/basetag.pas
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,9 @@ function TTagReader.GetDuration: Int64;

constructor TTagReader.Create(FileName: TfileName);
begin
// Create;
LoadFromFile(FileName);
FFileName := FileName;
if FileExists(FileName) then
LoadFromFile(FileName);
end;

constructor TTagReader.Create;
Expand Down
14 changes: 7 additions & 7 deletions src/playlist.pas
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ TPlayList = class(TFPList)
procedure LoadAllTags;
procedure Shuffle;
Procedure Sort(Field: TplSortField; Direction:TplSortDirection); overload;
Procedure Sort; overload;
procedure Sort(idxFrom, idxTo: Integer); overload;
procedure PushPos;
procedure PopPos;
Property RepeatMode: TplRepeat read FRepeatMode write SetRepeatMode;
Expand Down Expand Up @@ -161,13 +161,13 @@ function TPlayList.EnqueueFile(FileName: TFileName): integer;
var
Song: TCustomSong;
begin
if FileExists(FileName) then
// if FileExists(FileName) then
begin
song := TCustomSong.Create(FileName);
Result := Add(Song);
end
else
Result := -1;
// else
// Result := -1;
end;

function TPlayList.Add(ASong: TCustomSong): integer;
Expand Down Expand Up @@ -428,11 +428,11 @@ procedure TPlayList.Shuffle;

end;

procedure TPlayList.Sort;
procedure TPlayList.Sort(idxFrom, idxTo:Integer);
begin
PushPos;
if Assigned(self.List) then
IntQuickSort(Self.List, 0, count-1, @MyCompare);
IntQuickSort(Self.List, idxFrom, idxTo, @MyCompare);

PopPos;
end;
Expand All @@ -456,7 +456,7 @@ procedure TPlayList.Sort(Field: TplSortField; Direction:TplSortDirection);
begin
FSortField := Field;
fSortDirection := Direction;
Sort;
Sort(0, Count-1);
end;

end.
Loading

0 comments on commit 0d9887e

Please sign in to comment.