Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 4 038

/ вопрос открыт /

Здравствуйте, уважаемые эксперты!
подскажите как устроен многоуровневый Undo в текстовых редакторах. точнее как его реализовать через стандартный компонент (TRichEdit).

или помогите исправить ошибку в нижепредставленном коде...

Приложение:
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs, StdCtrls, Buttons, JvExButtons, JvButtons, JvExStdCtrls,
  8. JvRichEdit, ComCtrls,Contnrs;
  9.  
  10. type
  11. TForm1 = class(TForm)
  12. redt1: TRichEdit;
  13. btn2: TJvHTButton;
  14. procedure btn1Click(Sender: TObject);
  15. procedure redt1Change(Sender: TObject);
  16. procedure FormCreate(Sender: TObject);
  17. procedure FormDestroy(Sender: TObject);
  18. procedure btn2Click(Sender: TObject);
  19. private
  20.  
  21. { Private declarations }
  22. public
  23. { Public declarations }
  24. end;
  25.  
  26. var
  27. Form1: TForm1;
  28. UndoList: TObjectList;
  29. i:integer;
  30. implementation
  31.  
  32. {$R *.dfm}
  33.  
  34. procedure TForm1.btn1Click(Sender: TObject);
  35. begin
  36. redt1.Undo;
  37. end;
  38.  
  39. procedure TForm1.redt1Change(Sender: TObject);
  40. var
  41. s:TMemoryStream;
  42.  
  43. begin
  44. if redt1.Modified=true then begin
  45. s:=TMemoryStream.create;
  46. UndoList.add(s);
  47. redt1.Lines.SaveToStream(s);
  48. s.seek(0, soFromBeginning);
  49. i:=i+1;
  50. end;
  51. end;
  52.  
  53. procedure TForm1.FormCreate(Sender: TObject);
  54. begin
  55. i:=0;
  56. UndoList:= TObjectList.Create( True );
  57. end;
  58.  
  59. procedure TForm1.FormDestroy(Sender: TObject);
  60. begin
  61. UndoList.Free;
  62. end;
  63.  
  64. procedure TForm1.btn2Click(Sender: TObject);
  65. var
  66. S: TMemoryStream;
  67. begin
  68. if i<1 then exit;
  69. s:=UndoList[i-1] as TMemoryStream;
  70. redt1.lines.LoadFromStream(s);
  71. i:=i-1;
  72. end;


DeLF Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: DeLF (статус: Посетитель)
Вопрос отправлен: 15 апреля 2010, 01:25
Состояние вопроса: открыт, ответов: 0.


Мини-форум вопроса

Всего сообщений: 13; последнее сообщение — 15 апреля 2010, 14:47; участников в обсуждении: 2.
Вадим К

Вадим К (статус: Академик), 15 апреля 2010, 01:46 [#1]:

Код вроде ничего, вот только не вижу сохранение текущей позиции и выделения, а также освобождения ресурсов. TMemoryStream создаем, а кто его удалять будет?

И как именно этот код "неработает"?
Галочка "подтверждения прочтения" - вселенское зло.
DeLF

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

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

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

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

DeLF (статус: Посетитель), 15 апреля 2010, 14:17 [#10]:

так работает. как теперь сделать операцию обратного возврата по списку(redo). и еще. почему после первого нажатия кнопки нечего не убирается, но курсор переходит в начало текста, а лишь со второго нажатия начинается отмена действий.
Вадим К

Вадим К (статус: Академик), 15 апреля 2010, 14:37 [#11]:

вот здесь http://rsdn.ru/?article/dotnet/backforward.xml есть статья, которая рассказывает, как такие вещи организовываются. и хотя там на .NET, но концепция должна быть понятной.

Единственное, что я посоветую уже сейчас - это завести класс, который будет включать в себя и TMemoryStream для хранения текста, так и другие поля для хранения данных (позиции курсора, размера выделения)
Галочка "подтверждения прочтения" - вселенское зло.
DeLF

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/
Галочка "подтверждения прочтения" - вселенское зло.

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:42
Выполнено за 0.04 сек.