|
Вопрос # 4 038/ вопрос открыт / |
|
Здравствуйте, уважаемые эксперты!
подскажите как устроен многоуровневый Undo в текстовых редакторах. точнее как его реализовать через стандартный компонент (TRichEdit).
или помогите исправить ошибку в нижепредставленном коде...
Приложение: Переключить в обычный режим- unit Unit1;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, Buttons, JvExButtons, JvButtons, JvExStdCtrls,
- JvRichEdit, ComCtrls,Contnrs;
-
- type
- TForm1 = class(TForm)
- redt1: TRichEdit;
- btn2: TJvHTButton;
- procedure btn1Click(Sender: TObject);
- procedure redt1Change(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- procedure btn2Click(Sender: TObject);
- private
-
- { Private declarations }
- public
- { Public declarations }
- end;
-
- var
- Form1: TForm1;
- UndoList: TObjectList;
- i:integer;
- implementation
-
- {$R *.dfm}
-
- procedure TForm1.btn1Click(Sender: TObject);
- begin
- redt1.Undo;
- end;
-
- procedure TForm1.redt1Change(Sender: TObject);
- var
- s:TMemoryStream;
-
- begin
- if redt1.Modified=true then begin
- s:=TMemoryStream.create;
- UndoList.add(s);
- redt1.Lines.SaveToStream(s);
- s.seek(0, soFromBeginning);
- i:=i+1;
- end;
- end;
-
- procedure TForm1.FormCreate(Sender: TObject);
- begin
- i:=0;
- UndoList:= TObjectList.Create( True );
- end;
-
- procedure TForm1.FormDestroy(Sender: TObject);
- begin
- UndoList.Free;
- end;
-
- procedure TForm1.btn2Click(Sender: TObject);
- var
- S: TMemoryStream;
- begin
- if i<1 then exit;
- s:=UndoList[i-1] as TMemoryStream;
- redt1.lines.LoadFromStream(s);
- i:=i-1;
- end;
 |
Вопрос задал: DeLF (статус: Посетитель)
Вопрос отправлен: 15 апреля 2010, 01:25
Состояние вопроса: открыт, ответов: 0.
|
Мини-форум вопроса
Всего сообщений: 13; последнее сообщение — 15 апреля 2010, 14:47; участников в обсуждении: 2.
|
Вадим К (статус: Академик), 15 апреля 2010, 01:46 [#1]:
Код вроде ничего, вот только не вижу сохранение текущей позиции и выделения, а также освобождения ресурсов. TMemoryStream создаем, а кто его удалять будет?
И как именно этот код "неработает"?
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 01:53 [#2]:
он делает отмену только после первого нажатия на кнопку, а после второго удаляет всё содержимое RichEdit. как правильно оформить сохранение текущей позиции и выделения, и куда записать освобождение TMemoryStream?
|
|
Вадим К (статус: Академик), 15 апреля 2010, 10:30 [#3]:
Освобождение - здесь
if i<1 then exit;
s:=UndoList[i-1] as TMemoryStream;
redt1.lines.LoadFromStream(s);
i:=i-1;
//а это нужные строки!
s.free;
UndoList.delete(i);//подозреваю, проблема тут и кроется.
Если проанализировать детальнее, то получается, что новый элемент был добавлен в список, а счетчик указывает где то в середину. Заработает, расскажу об остальном.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 12:54 [#4]:
если if i<1 then exit;
s:=UndoList[i-1] as TMemoryStream;
redt1.lines.LoadFromStream(s);
i:=i-1;
s.free;
UndoList.delete(i);
то после второго нажатия на кнопку вобще выскакивает acces voilation. чтото видимо не так, или удаляет больше, чем должно
|
|
Вадим К (статус: Академик), 15 апреля 2010, 13:18 [#5]:
стоп, там же TObjectList с параметром ownObject = true;
Он сам удалит объект по факту удаления с списка. значит строку s.free; нужно убрать.
Второе - я бы отказался от счетчика - он не нужен. можно просто воспользоваться свойством Count.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 13:28 [#6]:
обьясните пожалуйста как пользоватся Count, и куда его втуливать. я начинающий программист
|
|
Вадим К (статус: Академик), 15 апреля 2010, 13:42 [#7]:
count - это свойство TObjectList, показывает сколько элементов есть в списке. Исходя из этого код наверно должен выглядеть так.
procedure TForm1.redt1Change(Sender: TObject);
var
s:TMemoryStream;
begin
if redt1.Modified then begin //здесь =true не нужно, это по индусски:)
s:=TMemoryStream.create;
redt1.Lines.SaveToStream(s);
s.seek(0, soFromBeginning);
UndoList.add(s);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
UndoList:= TObjectList.Create( True );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UndoList.Free;
end;
procedure TForm1.btn2Click(Sender: TObject);
var
S: TMemoryStream;
begin
if UndoList.count > 0 then begin
s:=UndoList[UndoList.count-1] as TMemoryStream;
assert(s <> nil);//защита от неверного объекта
redt1.lines.LoadFromStream(s);
UndoList.delete(UndoList.count - 1);
end;
end;
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 13:53 [#8]:
это всеровно ничего не поменяло. всеровно после второго btn2.click весь текст стирается. может оно после первого нажатия стирает весь список действий, или всё что в нём было. или нажатие кнопки снова вызывает onchange
|
|
Вадим К (статус: Академик), 15 апреля 2010, 13:59 [#9]:
Загрузка текста в редактор - да, конечно вызывает.
Пробуем так
if UndoList.count > 0 then begin
s:=UndoList[UndoList.count-1] as TMemoryStream;
assert(s <> nil);//защита от неверного объекта
redt1.OnChange := nil;
redt1.lines.LoadFromStream(s);
redt1.Modified := False;
redt1.OnChange := redt1Change;
UndoList.delete(UndoList.count - 1);
end;
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 14:17 [#10]:
так работает. как теперь сделать операцию обратного возврата по списку(redo). и еще. почему после первого нажатия кнопки нечего не убирается, но курсор переходит в начало текста, а лишь со второго нажатия начинается отмена действий.
|
|
Вадим К (статус: Академик), 15 апреля 2010, 14:37 [#11]:
вот здесь http://rsdn.ru/?article/dotnet/backforward.xml есть статья, которая рассказывает, как такие вещи организовываются. и хотя там на .NET, но концепция должна быть понятной.
Единственное, что я посоветую уже сейчас - это завести класс, который будет включать в себя и TMemoryStream для хранения текста, так и другие поля для хранения данных (позиции курсора, размера выделения)
Галочка "подтверждения прочтения" - вселенское зло.
|
|
DeLF (статус: Посетитель), 15 апреля 2010, 14:39 [#12]:
про класс не понял, а в остальном спасибо. помогло
|
|
Вадим К (статус: Академик), 15 апреля 2010, 14:47 [#13]:
ну... где то так
type
TCommand = class
public
property ms:TMemoryStream;
property selStart:integer;
property selLength:integer;
constructor Create;
destructor destroy; override;
end;
////
constructor TCommand.Create();
begin
ms := TMemoryStream.Create;
end;
destructor TCommand.Destroy();
begin
ms.free;
end;
Это набросок идеи. В списке сохраняем объекты такого вида. В будущем это даст упрощение работы.
по поводу того, что с первой попытки не отменяет.
Видимо, где то затерялось лишнее добавление в список. Залогируйте его. Если не знаете как - https://www.delphi-int.ru/articles/40/
Галочка "подтверждения прочтения" - вселенское зло.
|
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|