📄 galnklist.pas
字号:
FFirstNode := FTail;
if FLastNodeWasBound then
FLastNode := FHead;
end;
// FCount := 0; Do not the count as for mirror list the count is always -1
// if the list had strict first or last node, then correct them so that the
// list would not start growing
end;
end;
procedure TgaSharedDoubleList.DataOwnerDestroyed;
{:
procedure DataOwnerDestroyed is called when the DataOwner list is destroyed.
As all list data is also destroyed, the list is marked as "invalid"
}
begin
FDataOwner := nil;
FHead := nil;
FTail := nil;
FFirstNode := nil;
FLastNode := nil;
FCursor := nil;
end;
procedure TgaSharedDoubleList.InitDoubleList;
{:
procedure InitDoubleList overrides inherited InitDoubleList.
If the list is DataOwner, the inherited InitDoubleList is called.
If it is not, the Cursor it inited to the Head node and the Count is set to -1 -
tom indicate that the count is not valid for the mirror list
(all other initialization is done in CreateMirror constructor)
}
begin
if IsDataOwner then
inherited InitDoubleList
else begin
{set the cursor to the head node}
FCursor := FHead;
FCount := -1;
end;
end;
procedure TgaSharedDoubleList.InsertAfterCurrent(AItem: Pointer;
MoveCursorToNewItem: boolean);
{:
procedure InsertAfterCurrent overrides inherited InsertAfterCurrent.
Reason: to allow the mirroring list with strict start or end position to
"absorb" a item if the current item is the last one in the list
}
var
FirstNodeWasUnbound: Boolean;
begin
// Unbind the first node
FirstNodeWasUnbound := FFirstNode = FTail;
if FirstNodeWasUnbound then
FFirstNode := nil;
if FLastNode <> Cursor then
inherited InsertAfterCurrent(AItem, MoveCursorToNewItem)
else begin
FLastNode := InternalInsert(AItem, Cursor);
FTail := FLastNode^.dllnNext;
if MoveCursorToNewItem then
FCursor := FLastNode;
end;
// rebind first node if it was bound before
if FirstNodeWasUnbound then
FFirstNode := Head^.dllnNext;
end;
procedure TgaSharedDoubleList.Notify(Ptr: Pointer; Action: TListNotification);
{:
procedure Notify overrides inherited Notify calls inherited Notify.
Also calls the dataowners Notify, if the lis is mirror list
}
begin
inherited Notify(Ptr, Action);
if not IsDataOwner then
DataOwner.Notify(Ptr, Action);
end;
procedure TgaSharedDoubleList.NotifyMasterChanged(ANode: PdllNode; Action:
TgaNodeNotification);
{:
procedure NotifyMasterChanged.
Occurs for mirroring lists if the list is in ActiveDataShare state,
This method should be dispatched after the NotifyNodeChange is finished
ANode - a node which is changing, Action - action to be performed.
}
begin
case Action of
nnAdded: begin
if ANode^.dllnNext = FFirstNode then
begin
FHead := ANode;
if ANode^.dllnPrev = FLastNode then
FLastNode := ANode;
end;
if ANode^.dllnPrev = FLastNode then
FTail := ANode;
end;
nnDeleted: begin
if ANode = FCursor then
if Eof then
Last
else
Next;
if ANode = FTail then
if Assigned(FLastNode) then
FTail := FLastNode^.dllnNext
else
FTail := ANode^.dllnNext;
if ANode = FHead then
if Assigned(FFirstNode) then
FHead := FFirstNode^.dllnPrev
else
FHead := ANode^.dllnPrev;
if ANode = FLastNode then
if FFirstNode = Tail then
FlastNode := Head
else
FLastNode := FLastNode^.dllnPrev;
if ANode = FFirstNode then
if FLastNode = Head then
FFirstNode := Tail
else
FFirstNode := FFirstNode^.dllnNext;
end;
nnCleared: begin
FHead := DataOwner.Head;
FTail := DataOwner.Tail;
if Assigned(FFirstNode) then
FFirstNode := FTail;
if Assigned(FLastNode) then
FLastNode := FHead;
FCursor := Head;
end;
end;
FNodeRevID := DataOwner.NodeRevID;
FCursorNodeRev := DataOwner.NodeRevID;
end;
procedure TgaSharedDoubleList.NotifyNodeChange(ANode: PdllNode; Action:
TgaNodeNotification);
{:
procedure NotifyNodeChange overrides inherited NotifyNodeChange.
If list IsDataOwner, the calls inherited NotifyNodeChange, otherwise calls
DataOwners NotifyNodeChange
}
begin
if IsDataOwner then
inherited NotifyNodeChange(ANode, Action)
else
DataOwner.NotifyNodeChange(ANode, Action);
end;
procedure TgaSharedDoubleList.RemoveActiveDataUser(ADataUser:
TgaSharedDoubleList);
{:
procedure RemoveActiveDataUser overrides inherited RemoveActiveDataUser.
Calls inherited RemoveActiveDataUser if the list is data owner, otherwise
raises an exception.
}
begin
if not IsDataOwner then
raise EgaLinkListError.CreateFmt(SListIsNotDataOwner, [SCantRemoveActiveDataUser]);
inherited RemoveActiveDataUser(ADataUser);
end;
procedure TgaSharedDoubleList.RemoveDataUser(ADataUser: TgaSharedDoubleList);
{:
procedure RemoveDataUser overrides inherited RemoveDataUser.
Calls inherited RemoveDataUser if the list is data owner, otherwise
raises an exception.
}
begin
if not IsDataOwner then
raise EgaLinkListError.CreateFmt(SListIsNotDataOwner, [SCantRemoveDataUser]);
inherited RemoveDataUser(ADataUser);
end;
procedure TgaSharedDoubleList.SetActiveDataShare(Value: Boolean);
{:
SetActiveDataShare is the write access method of the ActiveDataShare property.
The ACtiveDataShare can be changed only if the list is a mirror list.
If ActiveDataShare is disabled, the list is removed from
DataOwners.ActiveDataUsers list.
If ActiveDataShare is enabled, the list borders and cursor are validated, and
the list is added to the DataOwners ActiveDataUsers list.
}
begin
if FActiveDataShare <> Value then
begin
if IsDataOwner then
raise EgaLinkListError.CreateFmt(SListIsDataOwner, [SCantChangeActiveDataShare]);
if Value then
begin
ValidateListBorders;
ValidateCursor(True);
DataOwner.AddActiveDataUser(Self);
end else
DataOwner.RemoveActiveDataUser(Self);
FActiveDataShare := Value;
end;
end;
procedure TgaSharedDoubleList.SetEndPos(APositionedList: TgaDoubleList;
StrictEndPos: Boolean);
{:
procedure SetEndPos - Sets the lits's last item to be the current item of the
APostionedList. If StrictEnPos is true, the items inserted to the DataOwnerList
between the last item and the item after that, are not accompanied to this list.
If StrictEnPos is False, all items added before the item next to the last item
set are absorbed into the list.
If the PostionedList is at Eof, then StrictEndPos do not have any effect.
APositionedList can be self.
}
begin
if IsDataOwner then
raise EgaLinkListError.CreateFmt(SListIsNotAMirroringList, [SCantSetTheStartOrEndPostions]);
if (DataOwner <> APositionedList) and
((APositionedList is TgaSharedDoubleList) and
(DataOwner <> (APositionedList as TgaSharedDoubleList).DataOwner)) then
raise EgaLinkListError.CreateFmt(SListsDoNotShareData, [SCantSetTheStartOrEndPostions]);
if APositionedList.Eof then
begin
FTail := APositionedList.Cursor;
FLastNode := nil;
end else
begin
FTail := APositionedList.FCursor^.dllnNext;
if StrictEndPos then
FLastNode := FTail^.dllnPrev
else
FLastNode := nil;
end;
// the node rev id-s must be reste before the validate
FNodeRevID := 0;
FCursorNodeRev := 0;
ValidateListBorders;
ValidateCursor(True);
end;
procedure TgaSharedDoubleList.SetStartPos(APositionedList: TgaDoubleList;
StrictStartPos: boolean);
{:
procedure SetStartPos - Sets the lits's first item to be the current item of the
APostionedList. If StrictStartPos is true, the items inserted to the
DataOwnerList between the first item and the item before that, are not
accompanied to this list. If StrictEnPos is False, all items added after the
item previous to the last item set are absorbed into the list.
If the PostionedList is at Bof, then StrictStartPos do not have any effect.
APositionedList can be self.
}
begin
if IsDataOwner then
raise EgaLinkListError.CreateFmt(SListIsNotAMirroringList, [SCantSetTheStartOrEndPostions]);
if (DataOwner <> APositionedList) and
((APositionedList is TgaSharedDoubleList) and
(DataOwner <> (APositionedList as TgaSharedDoubleList).DataOwner)) then
raise EgaLinkListError.CreateFmt(SListsDoNotShareData, [SCantSetTheStartOrEndPostions]);
if APositionedList.Bof then
begin
FHead := APositionedList.Cursor;
FFirstNode := nil;
end else
begin
FHead := APositionedList.Cursor^.dllnPrev;
if StrictStartPos then
FFirstNode := FHead^.dllnNext
else
FFirstNode := nil;
end;
// the node rev id-s must be reste before the validate
FNodeRevID := 0;
FCursorNodeRev := 0;
ValidateListBorders;
ValidateCursor(True);
end;
procedure TgaSharedDoubleList.ValidateCursor(CorrectIfInvalid: Boolean);
{:
procedure ValidateCursor - Checks the cursor of the list. If it is postioned
incorrectly, the if CorrectIfInvalid = True, Cursor is set to be at the Bof.
If False, an exception is raised.
}
begin
if FCursorNodeRev <> DataOwner.NodeRevID then
begin
if not IsCursorCorrect then
begin
if CorrectIfInvalid then
FCursor := Head
else
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, [SInvalidCursor]);
end;
FCursorNodeRev := DataOwner.NodeRevID;
end;
end;
procedure TgaSharedDoubleList.ValidateListBorders;
{:
procedure ValidateListBorders - checks if the list Tail and Head are inside the
dataowners list, and that the Strict astrt and end postions are correctly
preserved.
Raises an exception if the list can't be validated
}
begin
if not IsDataOwner then
begin
{ if the Master list node Revision ID is not changed, exit - list is up to
date }
if NodeRevID = DataOwner.NodeRevID then
Exit;
{ Check wheter FFirstItem and FLastItem are part of master list. if are,
then Tail and Head can be silently set (As they are used only for
convience)}
if Assigned(FFirstNode) then
begin
if DataOwner.IsNodeInside(FFirstNode) then
begin
if FFirstNode = DataOwner.Head then
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, [SStrictFirstNodePointsToTheHeadOfMasterList]);
FHead := FFirstNode^.dllnPrev;
if (FFirstNode = FTail) and Assigned(FLastNode) then
FLastNode := FHead;
end else
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, [SStrictFirstNodeNotPartOfTheMasterList]);
end else
if not DataOwner.IsNodeInside(FHead) then
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, [SHeadNodeNotInMasterList]);
if Assigned(FLastNode) then
FTail := FLastNode^.dllnNext;
{ begin
it is not strictly required to check wheter the tail node is inside the
master list. As the head node is (verified), then if
IsList(Head, Tail) = True, and provided that the master list is not
corrupted, the last node has to be inside master list
if DataOwner.IsNodeInside(FLastNode) then
begin
if FLastNode = DataOwner.Tail then
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, ['Strict last node is invalid - points to the Tail item of master list']);
FTail := FLastNode^.dllnNext;
end else
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, ['Strict last node is invalid - not part of the master list']);
end else
if not DataOwner.IsNodeInside(FTail) then
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, ['Tail node is invalid - not part of the master list']);}
if not IsList(FHead, FTail) then
raise EgaLinkListError.CreateFmt(SMirrorListValidateFailed, [SInvalidHeadOrTail]);
{ remember the last validate time }
FNodeRevID := DataOwner.NodeRevID;
end;
end;
{
**************************** TgaDoubleListBookmark *****************************
}
constructor TgaDoubleListBookmark.Create(AOwnerList: TgaDoubleList);
{:
Constructor Create overrides the inherited Create.
First inherited Create is called, then the internal data structure is
initialized to store tha list for which a bookmark was created and current
postion inside this list.
}
begin
inherited Create;
FOwnerList := AOwnerList;
FCursor := FOwnerList.Cursor;
FNodeRevID := FOwnerList.NodeRevID;
end;
function TgaDoubleListBookmark.GetIsValid: Boolean;
{:
function GetIsValid overrides inherited GetIsValid.
Returns: True, if the Bookmark is valid
False, if it is not.
}
begin
if NodeRevID = OwnerList.NodeRevID then
Result := True
else begin
Result := OwnerList.IsNodeInside(Cursor);
if Result then
FNodeRevID := OwnerList.NodeRevID;
end;
end;
function TgaDoubleListBookmark.GetItem: Pointer;
{:
function GetItem overrides inherited GetItem.
Returns: A pointer to the item represented by the Bookmark
}
begin
if not IsValid then
raise EgaListBookmarkError.CreateFmt(SInvalidBookmark, [SCantGetItem]);
Result := Cursor.dllnData;
end;
function TgaDoubleListBookmark.IsValidFor(AList: TgaDoubleList): Boolean;
{:
function IsValidFor checks if the bookmark is valid for a list represented by
the AList.
Returns: True, if the AList uses the same data as bookmarks owner list
(AList = OwnerList or AList.DataOwner = OwnerList.DataOwner, if this compare
is (partially) applicable), and the node represented by the bookmark's cursor is
inside the AList
}
begin
// use IsValid if possible as IsValid is more optimized
if AList = OwnerList then
Result := IsValid
else begin
if (AList is TgaSharedDoubleList) and (OwnerList is TgaSharedDoubleList) then
Result := TgaSharedDoubleList(OwnerList).DataOwner = TgaSharedDoubleList(AList).DataOwner
else if AList is TgaSharedDoubleList then
Result := OwnerList = TgaSharedDoubleList(AList).DataOwner
else if OwnerList is TgaSharedDoubleList then
Result := TgaSharedDoubleList(OwnerList).DataOwner = AList
else
Result := False;
if not Result then
Exit;
Result := AList.IsNodeInside(Cursor);
end;
end;
procedure TgaDoubleListBookmark.SetItem(Value: Pointer);
{:
procedure SetItem overrides inherited SetItem.
Changes the item represented by the bookmark to the new Value.
}
var
tmpItem: Pointer;
begin
if not IsValid then
raise EgaListBookmarkError.CreateFmt(SInvalidBookmark, [SCantSetItem]);
tmpItem := Cursor^.dllnData;
if tmpItem <> Value then
begin
Cursor^.dllnData := Value;
OwnerList.NotifyChange(tmpItem, Value);
end;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -