Created
November 16, 2013 05:24
-
-
Save w495/7496317 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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