Skip to content

Instantly share code, notes, and snippets.

@w495
Created November 16, 2013 05:24
Show Gist options
  • Save w495/7496317 to your computer and use it in GitHub Desktop.
Save w495/7496317 to your computer and use it in GitHub Desktop.
unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes,
SysUtils,
FileUtil,
Forms,
Controls,
Graphics,
Dialogs,
DBGrids,
ButtonPanel,
ExtCtrls,
StdCtrls, Buttons;
type
(*
Тип данных, как типизированный указатель для типа `TNode`.
*)
TNodePointer = ^TNode;
(*
Тип данных, как структуру (запись) `TNode`. Она состоит
из поля данных `Data: String` и указателя на аналогичную
структуру `Next: TNodePointer`.
*)
TNode = record
(*
Строка с данными.
*)
Data: String;
(*
Указатель на структуру.
*)
Next: TNodePointer;
end;
{ TListsForm }
TListsForm = class(TForm)
AddToTailButton: TBitBtn;
AddToHeadButton: TBitBtn;
(* Область рисования. *)
PaintBox: TPaintBox;
(* Процедура инициализации формы. *)
procedure FormCreate(Sender: TObject);
(* Процедура добавления узла списка в начало списка. *)
procedure AddToHeadButtonClick(Sender: TObject);
(* Процедура добавления узла списка в конец списка. *)
procedure AddToTailButtonClick(Sender: TObject);
(* Процедура перерисовки области рисования. *)
procedure PaintBoxPaint(Sender: TObject);
(* Процедура отрисовки списка *)
procedure DrawList(NodePointer: TNodePointer);
(* Процедура отрисовки узла списка *)
procedure DrawNode(X, Y, Size: Integer; Data: String);
(* Процедура отрисовки стрелки узла списка *)
procedure DrawArrow(Len, X, Y: Integer);
private
{ private declarations }
public
{ public declarations }
end;
const
DEFAULTNODESIZE = 50;
var
(* Зададим переменную формы. *)
ListsForm: TListsForm;
(* Зададим текущего узла списка. *)
HeadNodePointer: TNodePointer;
implementation
{$R *.lfm}
{ TListsForm }
(* Вызывается при создании формы.
Задает начальное значение для данного узла.
*)
procedure TListsForm.FormCreate(Sender: TObject);
begin
(* `NIL` означает "Not In List" *)
HeadNodePointer := NIL;
end;
(* Добавляет элемент в голову списка.
Вызывается при клике на кнопку `AddToHeadButton`.
Выводит окно с просьбой ввести текст. После ввода текста создает
новый узел списка с добавлением этого узла в голову (начало) списка.
В данном случае, новый элемент списка, становится первым (головой),
a тот элемент, который был первым элементом списка, становится вторым.
*)
procedure TListsForm.AddToHeadButtonClick(Sender: TObject);
var (* Строка для ввода *)
Input: String;
(* Указатель на новый узел. *)
NewNodePointer: TNodePointer;
begin
(* Вызываем окно ввода текста.
Введенный текст возвращается в переменную `Input`.
*)
Input := InputBox(
(* Заголовок окна. *)
'Ввод текста для элемента в голову списка',
(* Сообщение окна. *)
'Пожалуйста, введите, текст для содержимого узла',
(* Текст в окне по умолчанию. *)
'0'
);
(* Если на вход подается не пустая строка. *)
if('' <> Input) then
begin
(* Выделяем память для нового узла. *)
New(NewNodePointer);
(* Задаем поле данных для нового узла. *)
NewNodePointer^.Data := Input;
(* Задаем следующий элемент для нового узла.
Следующим элементом становится голова списка.
*)
NewNodePointer^.Next:= HeadNodePointer;
(* Новый узел становится головой списка. *)
HeadNodePointer := NewNodePointer;
end;
end;
(* Добавляет элемент в хвост списка.
Вызывается при клике на кнопку `AddToTailButton`.
Выводит окно с просьбой ввести текст. После ввода текста создает
новый узел списка с добавлением этого узла в хвост (конец) списка.
В данном случае, новый элемент списка, становится последним,
a тот элемент, который был последним элементом списка,
становится предпоследним.
*)
procedure TListsForm.AddToTailButtonClick(Sender: TObject);
var (* Строка для ввода *)
Input: String;
(* Указатель на новый узел. *)
NewNodePointer: TNodePointer;
(* Указатель на последний элемент списка. *)
LastNodePointer: TNodePointer;
(* Временный указатель для поиска последнего элемента списка. *)
TmpNodePointer: TNodePointer;
begin
(* Вызываем окно ввода текста.
Введенный текст возвращается в переменную `Input`.
*)
Input := InputBox(
(* Заголовок окна. *)
'Ввод текста для элемента в хвост списка',
(* Сообщение окна. *)
'Пожалуйста, введите, текст для содержимого узла',
(* Текст в окне по умолчанию. *)
'0'
);
(* Если на вход подается не пустая строка. *)
if('' <> Input) then
begin
(*
Ищем хвост списка. От головы списка проходим до самого конца,
до тех пор пока не встретим `NIL`.
На каждом шаге запоминаем текущий узел.
Если вдруг оказалось, что `TmpNodePointer = NIL`, то указатель,
который на него указывал, и есть последний элемент списка.
*)
LastNodePointer := NIL;
(* Начинаем обход от головы. *)
TmpNodePointer := HeadNodePointer;
while TmpNodePointer <> NIL do
begin
(* Текущим указателем цикла становится указатель на следующий узел. *)
LastNodePointer := TmpNodePointer;
TmpNodePointer := TmpNodePointer^.Next;
end;
(* Выделяем память для нового узла. *)
New (NewNodePointer);
(* Задаем поле данных для нового узла. *)
NewNodePointer^.Data := Input;
(* Т.к. новый элемент должен стать хвостом, то следующего от него уже нет.
Потому делаем указатель не следующий элемет ` = NIL`.
*)
NewNodePointer^.Next := NIL;
(* Если найденный последний элемент списке не `NIL`,
то делаем следующий от него элемент равным `NewNodePointer`.
*)
if(LastNodePointer <> NIL) then
LastNodePointer^.Next:= NewNodePointer
else
(* Иначе, т.е. если `LastNodePointer = NIL`,
то новый узел становится головой списка.
`LastNodePointer = NIL`, возможно только в том случае
если в списке еще нет элементов.
*)
HeadNodePointer := NewNodePointer;
end;
end;
(* Перерисовывает область рисования.
*)
procedure TListsForm.PaintBoxPaint(Sender: TObject);
begin
(*
При перерисовке --- рисуем список начиная от головы списка.
*)
Self.DrawList(HeadNodePointer)
end;
(* Рисует список. Входной параметр --- указатель на узел списка.
Идея: проходит по списку от текущего узла, до тех пор,
пока не встретит указатель внекуда `NIL`.
*)
procedure TListsForm.DrawList(NodePointer: TNodePointer);
var (* Координата узла по оси `x`. *)
CoordX: Integer;
(* Разница смещения на каждом шаге. *)
DrawOffsetDelta: Integer;
(* Размер узла при отрисовке. *)
NodeSize: Integer;
begin
(* Начальная координата узла по `x` равна `0`. *)
CoordX := 0;
(* Размер узла задаем равным константе `DEFAULTNODESIZE` *)
NodeSize := DEFAULTNODESIZE;
(* Разницу смещения на каждом шаге зададим как полтора размера узла.
Именно такой размер нам нужен всилу того, что размер стрелки
мы задали как половину размера (читай "длины") узла.
*)
DrawOffsetDelta := NodeSize + trunc(NodeSize / 2);
(* Выполняет цикл до тех пор пока `NodePointer` не равен `NIL` *)
while NodePointer <> NIL do
begin
(* Рисуем один узел --- текущий для данного цикла. *)
Self.DrawNode(CoordX, 0, NodeSize, NodePointer^.Data);
(* Задаем координату по оси 'x' для слудущего узла *)
CoordX := CoordX + DrawOffsetDelta;
(* Текущим указателем цикла становится указатель на следующий узел. *)
NodePointer:= NodePointer^.Next;
end;
end;
(* Рисует узел списка со стрелкой. Входные параметры:
X --- начальная координата узла (левый верхний угол) по оси x.
Y --- начальная координата узла (левый верхний угол) по оси y.
*)
procedure TListsForm.DrawNode(X, Y, Size: Integer; Data: String);
begin
(* Рисуем эллипс. Напоминаю, что эллипс рисуется по левой верхней
и правой нижней точкам прямоугольника, в который вписан этот эллипс.
*)
{Self.PaintBox.Canvas.Rectangle(X, Y, X + Size, Y + Size);}
Self.PaintBox.Canvas.Ellipse(X, Y, X + Size, Y + Size);
(* Рисуем стрелку. *)
Self.DrawArrow(
trunc(Size / 2), (* --- длина стрелки. *)
X + Size, (* --- начальная координата стрелки по оси `x`. *)
Y + trunc(Size / 2) (* --- начальная координата стрелки по оси `y`. *)
);
(* Рисуем текстовый блок с именем узла списка. *)
Self.PaintBox.Canvas.TextOut(
(* Координата `x` для текстового блока. *)
X + trunc(Size / 2) - trunc(Self.PaintBox.Canvas.TextWidth(Data) / 2),
(* Координата `y` для текстового блока. *)
Y + trunc(Size / 2) - trunc(Self.PaintBox.Canvas.TextHeight(Data) / 2),
(* Сообщение текстового блока. *)
Data
);
end;
(* Рисует горизональную стрелку. Входные параметры:
Len --- длина стрелки.
X --- начальная координата стрелки по оси x.
Y --- начальная координата стрелки по оси y.
*)
procedure TListsForm.DrawArrow(Len, X, Y: Integer);
var NewX: Integer;
begin
NewX := X + Len;
(* Рисуем большую горизотальную линию. *)
Self.PaintBox.Canvas.Line(X, Y, NewX, Y);
(* Рисуем верхний лепесток стрелки. *)
Self.PaintBox.Canvas.Line(NewX, Y, NewX - 10, Y + 3);
(* Рисуем нижний лепесток стрелки. *)
Self.PaintBox.Canvas.Line(NewX, Y, NewX - 10, Y - 3);
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment