Undo for Form Designer working? lazarus / 10-Designer ---------------------------------------------------------------------- IPguy · 2011-03-27 01:52 I have been unable to get the undo to work for changes to my forms. Is that function available in Lazarus? ---------------------------------------------------------------------- Blaazen · 2011-03-27 02:42 There is no undo function for Form Designer. If you delete component from your form you cannot undo it (get the deleted component back). Undo is for Source Editor only. ---------------------------------------------------------------------- JuhaManninen · 2011-03-27 13:59 Quote from: IPguy on March 27, 2011, 03:52:46 amI have been unable to get the undo to work for changes to my forms. Is that function available in Lazarus?  http://bugs.freepascal.org/view.php?id=1579 I also missed that feature so much that I started to implement it > a year ago. First I forwarded the Undo and Redo commands to form designer. It worked but also screwed the whole internal command dispatch system. There are some complex dependencies. Implementing the designer undo itself is rather difficult, too. The designer is a complicated piece of SW. If someone dives in and implements this feature, I will give him 10 points in cool-factor scale :-) It i possible to do of course, it is just difficult. BTW, even Delphi doesn't have this feature, even after years of similar open feature requests. Juha ---------------------------------------------------------------------- Blaazen · 2011-03-27 14:10 Maybe simplier solution would be a trash for deleted components (the same what is for files in all OS) instead of classic Undo/Redo. I understand it is very difficult because of grouping components (TGroupBox, TPanel or TPageControl). ---------------------------------------------------------------------- IPguy · 2011-03-27 16:32 "trash for deleted components" would solve a large portion of the instances where I've attempted to "undo". ---------------------------------------------------------------------- typo · 2011-03-27 17:24 Is there this issue on BugTracker? Maybe requesting it should attract some interest. ---------------------------------------------------------------------- JuhaManninen · 2011-03-27 17:53 Quote from: typo on March 27, 2011, 07:24:32 pmIs there this issue on BugTracker? Maybe requesting it should attract some interest. Yes, see my message above. There is a link. I believe there is enough interest for this feature but nobody has enough time/energy/knowledge or whatever to implement it. If someone is interested, please look at "designer/designer.pp" + the units in its uses section. @Blaazen & IPguy: How would "trash for deleted components" work? So, the deleted components could be restored from the trash? Hey, that actually is called "Undo" feature. Maybe it is a one-level Undo, and maybe it works only for delete- operation, but still it is Undo. I don't think implementing this stripped-down Undo feature is much easier than doing it properly. You must understand much of the form designer and cmd dispatch system anyway. At the same go you should do it properly, IMO. Intermediate goals can be set to "one-level-undo" or "undo-for-delete" of course but the design should support a full Undo implementation. Juha ---------------------------------------------------------------------- typo · 2011-03-27 18:26 Which approach did you use? Did you save somehow the whole LFM file at each operation or only the operation itself? ---------------------------------------------------------------------- Blaazen · 2011-03-27 18:39 QuoteHow would "trash for deleted components" work?Similarly to a trash for file in OS (I use KDE4). You can have thousands files in your trash and you can select any file to restore. You don't have to restore files in order 'last-in ~ first out' (trash is not stack). It is difference to classic Undo function. Another example is bash-history. Pressing up-arrow key you can list between previously used commands. When you would restore any components from such trash it would be similar to adding a new component. Only differce would be that its properties and events will not be initial. And next problem - also similar to files. When I try restore file to a directory which is already deleted then I get warning: 'Directory XYZ does not exists. You can restore file to other directory.' The same would be when you have Button on Panel. You delete Button, you delete Panel and then you try restore Button. Finally I think undo for components is not a 'must have' function, at least for me. ---------------------------------------------------------------------- JuhaManninen · 2011-03-27 18:43 Quote from: typo on March 27, 2011, 08:26:31 pmWhich approach did you use? Did you save somehow the whole LFM file at each operation or only the operation itself? I really didn't get so far. I still don't understand enough of the designer code to make such decision. I THOUGHT I understood enough of the command passing code but then other parts of the IDE behaved strangely. The cmd forward patch is still in bug tracker. Mattias committed it but it had to be reverted quickly. Anyway, this task is something that takes time and effort unless you know the designer code well already (... and maybe 1 or 2 people know it well). Learn code, try something, learn more, try more... and so on... Juha ---------------------------------------------------------------------- JuhaManninen · 2011-03-27 19:09 Quote from: Blaazen on March 27, 2011, 08:39:39 pmThe same would be when you have Button on Panel. You delete Button, you delete Panel and then you try restore Button. Yes, that makes the task more difficult, not easier. Good ideas still. QuoteFinally I think undo for components is not a 'must have' function, at least for me. I would like the feature very much. It would make a big difference when you accidentally delete a container component with other comps inside, or you experiment with the layout and want to revert it. Anyway, someone just have to make the effort and implement it. Juha ---------------------------------------------------------------------- typo · 2011-03-27 19:27 Maybe the task can be divided in two parts: 1) Writing the code itself 2) Inserting it on the Form Designer (made by someone who knows the IDE code) And then the second subtask could be requested on BugTracker. ---------------------------------------------------------------------- typo · 2011-03-27 20:26 I have added a note on the BugTracker's issue. ---------------------------------------------------------------------- JuhaManninen · 2011-03-27 21:31 Quote from: typo on March 27, 2011, 09:27:53 pmMaybe the task can be divided in two parts: 1) Writing the code itself 2) Inserting it on the Form Designer (made by someone who knows the IDE code) To me that sounds very unrealistic, sorry. The difficult part is exactly to figure out how to interact with the complex designer code, and the cmd dispatch code. It is not like you write an independent Undo program without looking at designer code, and then someone else "inserts" it to Lazarus. Or, maybe I misundertood the idea, don't know... In practice, somebody must jump in the code back and forth, Ctrl-Click and Ctrl-H, and debug it, again and again so its logic and structure opens. And then based on that knowledge implement Undo. ... this brings me to another issue: Class- and other UML-diagrams, or lack of them. There is no documentation of the code internals. Sure, the programmer always must read the code to understand details but the big picture could be more easily understood from a good diagram. Another task for somebody... Juha ---------------------------------------------------------------------- typo · 2011-03-27 21:43 QuoteThe difficult part is exactly to figure out how to interact with the complex designer code, and the cmd dispatch code. It is not like you write an independent Undo program without looking at designer code, and then someone else "inserts" it to Lazarus. If you want to restore each operation, of course you need to know how exactly is that operation in order to reverse it. But you could restore the whole previous state of the form instead of reversing each operation. And then you don't need to know what exactly was done. So, the code would be the same for any operation and could be "inserted" or whatever. ---------------------------------------------------------------------- Martin_fr · 2011-03-27 23:39 undoing, or restoring the form/lfm is not enough. the source is also modified (components are added/deleted to the Form1 (or other) class); units are added; .... Those operations would have to be undone too ---------------------------------------------------------------------- IPguy · 2011-03-27 23:41 QuoteBut you could restore the whole previous state of the form instead of reversing each operation.Could this be done by saving the current instance of the form (.lfm) and showing the user which prior version of the form they want to revert to? The problem with this simple undo is that it does not affect the code.  It would work if form objects were moved around, but the application might have problems if several objects had been deleted then the user, after undo, expects the app to compile correctly with several of the original  Shape1:  TShape;   declarations missing (for example). ---------------------------------------------------------------------- IPguy · 2011-03-27 23:47 How about showing me (in a form sheet/window?) the specific instances of objects I've deleted and allowing me to drag / drop (or copy/paste) them back into my form? That way I can see which objects I've deleted, what the object customizations were (ie: labels, colors, size, shape,etc..). This undo would only be valid for the current instance of the editor and all of the saved undo objects would be cleaned up when the editor shuts down? I do realize this is not a trivial task and I can live without it, but it would be nice. Assuming nothing changes for a while, could the "undo" menu be disabled out when the focus is in a form and enabled when in an edit window?  At least we would set expectations about when Undo will not function. ---------------------------------------------------------------------- typo · 2011-03-27 23:49 QuoteThose operations would have to be undone too My point is: you could save the whole files, also the PAS file, before each operation and restore them back instead of reverting the operation. If so, even the uses section should be restored, which not occurs if one deletes a component. I don't know how it would interact with undo feature of the code editor, however. QuoteThis undo would only be valid for the current instance of the editor and all of the saved undo objects would be cleaned up when the editor shuts down? Of course once one has closed the IDE, undo feature is gone. ---------------------------------------------------------------------- Blaazen · 2011-03-28 00:11 How is adding component done? I presume that when I click TButton on Component Palette and click the form then some TButton.Create is done for design-time form and *.lfm is generated later. ---------------------------------------------------------------------- typo · 2011-03-28 00:37 You can see it on designer/designer.pas, line 1880. ---------------------------------------------------------------------- Blaazen · 2011-03-28 01:31 Thanks. My idea is that trash would be a simple list whose items would be pointers to (not really) deleted components. When someone deletes any component then it will not be really deleted (like TButton.Free) but will stay created and pointer on it will be added to trash-list. Restoring component from trash is then similar to creating new component with at least three differencies: 1) Creating component is not necessary 2) Check if component's previous parent is still present 3) Check component's name duplicity. Adventage of this solution is that there is not necessary do any change with Source Editor. Of course user's can delete Panel with some components on it at once which is next problem. ---------------------------------------------------------------------- typo · 2011-03-28 01:57 My approach is similar to that used for undoing image editions. ---------------------------------------------------------------------- Martin_fr · 2011-03-28 02:08 Quote from: Blaazen on March 28, 2011, 03:31:18 amThanks. My idea is that trash would be a simple list whose items would be pointers to (not really) deleted components. When someone deletes any component then it will not be really deleted (like TButton.Free) but will stay created and pointer on it will be added to trash-list. That may cause issues too: Scenario Button1 and Button2; Button2's left side is anchored to Button1 (so the Anchorside has a pointer to button1) When you delete button1, then the anchor must be removed. AFAIK, that is done by "TComponent.Notification" (something automatically done, on destruction). So if you do not destroy, then you have an invalid anchor. And, independent of how you store the information, you must find such additional changes too, as undo should restore the anchor, I guess Quote Restoring component from trash is then similar to creating new component with at least three differencies: 1) Creating component is not necessary 2) Check if component's previous parent is still present 3) Check component's name duplicity. Check for anchors, check inheritance (if frames are involved, or visual form inheritance..., check events, scheck duplicate event names, datasource dependencies, other links to other components ... The list of dependencies is endless. On the other hand, a system that undoes every step (across all forms), then that ensures that at the time of an undo, the entire state is exactly as it was at the time of the action (since all changes after the action were undone) Quote Advantage of this solution is that there is not necessary do any change with Source Editor. How so? Unless you leave the line   Button1: TButton; If you delete button1, (even if you move it into a trash) you must remove this line. so you must restore it on undo? And thats another already existing issue. Any changes to the source, are part of SynEdits undo list (and must be, because synedit, stores undo in a way that requires them to be complete. So if you press undo in the edit, you may just delete the line   Button1: TButton; but the component remains..... You can not exclude the insertion of this line from synedit's undo info, or you loose all undo from before.... So the final goal would have to be one single unified undo system across the entire IDE; or at least for each source and form together (but then you need to ensure that form undo does not fail on frames etc). ------------------ Of course not all of it has to be done in one step... But you have to keep it in mind. ---------------------------------------------------------------------- Blaazen · 2011-03-28 12:05 I forgot the anchors. Yes, it is problem. QuoteQuote: Advantage of this solution is that there is not necessary do any change with Source Editor. How so? I was not clear. Advantage of this solution is that there is not necessary do any change IN WORKING with Source Editor. Current state: Add component:   Button1: TButton;  //line is added  (and possibly something to uses lists for some components) Delete component:   Button1: TButton;  //line is deleted (AFAIK uses lists is not affected) Trash: Restore component:   Button1: TButton;  //line is added  (and possibly something to uses lists for some components) Delete component to trash:   Button1: TButton;  //line is deleted (uses lists is not affected) So, no change. ---------------------------------------------------------------------- JuhaManninen · 2011-03-28 12:27 Quote from: IPguy on March 28, 2011, 01:47:19 amAssuming nothing changes for a while, could the "undo" menu be disabled out when the focus is in a form and enabled when in an edit window?  At least we would set expectations about when Undo will not function. This is a valid suggestion. I disabled Undo and Redo now when designer is active. Even this was kind of a hack because the command dispach system is a mess. Neither Undo/Redo nor Copy/Paste from menu are forwarded to form designer. It means you can use shortcuts but not menu items for Copy/Paste in designer. I hope the command system gets fixed and cleaned by the core developers. Otherwise this discussion has been rather useless. Sorry... I also have many "ideas" and "approaches" but they have no importance if I don't know how the existing code works and how I can implement those ideas and approaches. It is like : "Talk is cheap, show me the code" as one open-source person said. No offence meant for anybody personally. Regards, Juha ---------------------------------------------------------------------- Jishaxe · 2011-04-12 20:10 I swear once I deleted a component and pressed Ctrl+Z to restore it. Or maybe it was a dream :L ---------------------------------------------------------------------- Sanchoyzer · 2012-05-19 14:02 Good day. I want to implement a function of the complete undo\redo of the Form Designer. Excuse me, I write in the subject, which is not updated for a long time, but I think my thoughts are a logical extension of the existing messages. And sorry for my English. After reading the existing solutions, I have formed a 7 options, one of which I think to implement in the code: For the PAS: 1.    Changes:           insert  modified remove *. Pas    +       -             + *. Lfm    +       +             + Since the changes in the *. pas-file will only occur when adding or removing, the need to implement the inverse function + function text search (fast enough, such as the KMP). Inverse function of removal: find a place (Class), where to insert (using a text search), add the appropriate ad. The inverse adding: find a place (Class), which removed (using a text search), delete the row. 2. Divide all the time with Lazarus into 2 periods: the work in the form editor and the rest of the time. We believe that while the user in the form editor, it does not change the *. pas-file, so undo \ redo will operate only during the active form editor. When you switch to Form Designer ability to undo \ redo disappear. Changes in the code can be done the usual (already implemented) undo \ redo for the code editor. Or, to save every time you change the form *. pas-file and store the last N = N files change. For LFM: 1. We completely keep a list_1 (by time) lists_2 (by space of form) of objects. That is, with any operation, the new item of list1 will create, in which list_2 of all elements of the form with all fields \ methods. Undo \ redo is iterating on the list_1. 2. We keep a list of objects, ie at any operation object is added to this list, which changed its status \ add \ remove + the type of operation for him. Iteration is implemented undo \ redo functionality. By combining these methods, we obtain 2 * 2 = 4 = IV option. V) Since the changes in the *. lfm-file occur with any operation, it is necessary to maintain its integrity and relevance. We will store the last few versions (states) *. lfm. Undo \ redo will be realized through the implementation of diff over the two neighboring (in time) states of the form. So we find the fields\ method that has been modified \ add \ remove, and then all of the same text search find the right place in the pas-file. VI) Making the mechanism undo \ redo is not automatic, and controllable by the user: pressed save - save the state of the project (for example, by saving lfm- and pas-files), pressed undo - returned to the saved state. Thus we carry all the responsibility for maintaining the relevance and integrity on the shoulders of the user. In addition, you can make an optional auto-save after every certain number of minutes. VII) Pointer Relocation Map (PRM). Some way to add monitoring a pas- file to scheme PRM. In the process... These methods should maintain synchronization *. pas, and *. lfm files, and (in theory) there should be no situations with the disappearance of the code in the *. pas, and incorrect processing of contacts, such as anchors. Accordingly, to obtain the object's state will be using rtti: TPropInfo, TPropList and the like - from . Catch the change \ add \ remove the object will be using GlobalDesignHook: TPropertyEditorHook, which of the module , namely, using the functions like AddHandlerComponentRename, RemoveHandlerComponentRename, AddHandlerPersistentDeleting and other. We will have observe the state in the property OnModified from module . That's it. What will say the community? ---------------------------------------------------------------------- JuhaManninen · 2012-05-19 14:33 Sounds good! You will surely surprise me and some other people if you actually do it. Waiting to see your patch... Regards, Juha