| 
| 
 | Вопрос # 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);beginredt1.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);beginUndoList.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/
 Галочка "подтверждения прочтения" - вселенское зло. |  Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте. |