Recent

Author Topic: Help with implementing a MDI application using MultiFrame and SynEdit  (Read 779 times)

maurobio

  • Full Member
  • ***
  • Posts: 156
Dear ALL,

In the course of learning how to use some of the less well documented features of the SynEdit component editor, as well as the MultiFrame component for the simulation of 'classical' MDI applications, I have developed a simple program using these components, which I have called VIPER - The Very Ineffective Programming Editor for R. Althought this program is primarily intended to be just a demonstration for teaching/learning purposes, nonetheless it aims to offer a very simple, but fully functional, interface to the R system for statistical computing and graphics (https://www.r-project.org/).

VIPER demonstrates several features of SynEdit for which I have had a hard time to find out how to use, as a line/column counter on the status bar, search and replace using the common dialogs, turning line numbers on and off, and so on. In this effort, I have been much helped by the superb and highly recommended document 'La Biblia del SynEdit' written by Tito Hinostroza (http://blog.pucp.edu.pe/blog/tito/2013/10/12/lazarus-la-biblia-del-synedit/).

Using the MultiFrame component (https://github.com/lycaner/multiframe) written by Patrick Chevalley (author of two of the greatest Lazarus applications ever, the fantastic SkyChart and Virtual Moon Atlas) showed to be more difficult to master, because documentation for this component is quite scarce. In this regard, I really got a good start with the small demo provided in this forum by user PeterX (https://forum.lazarus.freepascal.org/index.php?topic=34137.0).

The implementation is fairly satisfactory, but I found a few problems which I hope to solve with your help:

i) How to detect when there is no more child windows on the program main window? I am using the MultiFrame event OnDeleteChild to detect when each window is closed, but it seems that it still returns '1' even after the last child is closed.

ii) I could not devise a way to detect when a file has been changed, issuing a warning to the user. The event OnChange of the SynEdit component allows to detecting changes in the text editor, but how can I pass this information to the main form?

iii) When calling R as an external process, by means of the RunCommand procedure (in the Process unit), an ugly console screen keeps flashing when R is executed, even if I added the poNoConsole parameter. This is really strange because I tested calling another command line program with the RunCommand procedure and in that case the console window has not been displayed.

iv) Since there is no predefined highlighter for the R language included in Lazarus, I have used the one for PHP until I can create one for R using the SynAnySyn highlighter. This is not really an issue, but if someone have already done that, it would avoid the hassle of reinventing the wheel.

I attach the complete source code of VIPER, as well as the lastest version of the MultiFrame component (which should be installed in order to be able to run the program).

Could someone give me some hints towards solving these issues?

Thanks in advance for any assistance you can provide.

Best regards,

UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

lucamar

  • Hero Member
  • *****
  • Posts: 3074
Re: Help with implementing a MDI application using MultiFrame and SynEdit
« Reply #1 on: November 03, 2019, 09:40:33 am »
ii) I could not devise a way to detect when a file has been changed, issuing a warning to the user. The event OnChange of the SynEdit component allows to detecting changes in the text editor, but how can I pass this information to the main form?

SynEdit has a Modified property that should tell you that; something like:

Code: Pascal  [Select][+][-]
  1. function TMainForm.EditorModified: Boolean;
  2. begin
  3.   if MultiFrame.ActiveObject is TFrameTemplate then
  4.     with MultiFrame.ActiveObject as TFrameTemplate do
  5.       Result := SynEdit.Modified
  6.   else
  7.     Result := False;
  8. end;

BTW, since that's a pattern that is repeated quite some times in your code I would add a method in the main form to return the current editor; someting like:

Code: Pascal  [Select][+][-]
  1. function TMainForm.CurrentEditor: TSynEdit;
  2. begin
  3.   if MultiFrame.ActiveObject is TFrameTemplate then
  4.     Result :=  (MultiFrame.ActiveObject as TFrameTemplate).SynEdit
  5.   else
  6.     Result := Nil;
  7. end;

so every time you need to access the editor you can do something like, for example:

Code: Pascal  [Select][+][-]
  1. function TMainForm.EditorModified: Boolean;
  2. begin
  3.     Result := Assigned(CurrentEditor) and CurrentEditor.Modified;
  4. end;
  5.  
  6. procedure TMainForm.EditUndoClick(Sender: TObject);
  7. begin
  8.   if Assigned(CurrentEditor) then
  9.     CurrentEditor.Undo;
  10. end;
  11.  
  12. {... and so on ...}
« Last Edit: November 03, 2019, 09:48:10 am by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.8/FPC 3.0.4 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

maurobio

  • Full Member
  • ***
  • Posts: 156
Re: Help with implementing a MDI application using MultiFrame and SynEdit
« Reply #2 on: November 03, 2019, 10:14:18 am »
Thank you very much, lucamar! Your tips are very helpful. The "Modified" property seems to be one of those less documented features of the SynEdit component which I hope to sort out with my demo editor/R IDE.

I hope now to get help with MultiForm itself to solve the problem of knowing how to get the true number of windows opened.

Cheers,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 6675
  • Debugger - SynEdit - and more
    • wiki
Re: Help with implementing a MDI application using MultiFrame and SynEdit
« Reply #3 on: November 03, 2019, 11:55:12 am »
Synedit also has
ChangeStamp: int64

which counts up (cycles) with each change. So you can store the last value, and see if it changed since.
IIRC cursor movement/selection does not count as change (but triggers OnChange).

Also IIRC "undo"
- adds another change to changestamp
- but can clear Modified.

I don't see why you can't act on OnChange? When you create the SynEdit (presumingly add runtime) just add the event handler, which can be on any other form. (You just have to remove it if the other form is destroyed before the SynEdit)

OnStatusChange will also give you caret move and more
Code: Pascal  [Select][+][-]
  1.   TSynStatusChange = (scCaretX, scCaretY,
  2.     scLeftChar, scTopLine, scLinesInWindow, scCharsInWindow,
  3.     scInsertMode, scModified, scSelection, scReadOnly,
  4.     scFocus,     // received or lost focus
  5.     scOptions    // some Options were changed (only triggered by some optinos)
  6.    );
  7.   TSynStatusChanges = set of TSynStatusChange;
  8. TStatusChangeEvent = procedure(Sender: TObject; Changes: TSynStatusChanges)

Note, there is no monitoring of the file on disk. If that changes you need to detect that outside of SynEdit

maurobio

  • Full Member
  • ***
  • Posts: 156
Re: Help with implementing a MDI application using MultiFrame and SynEdit
« Reply #4 on: November 03, 2019, 12:17:10 pm »
@Martin_fr, thank you for your comments. I also don't see why the OnChange event didn't work, but this is no more relevant to me as the "Modify" property pointed out by @lucamar works great.

Notice that this program is intended as a very simple demo, aimed at helping myself, and hopefully others, to learn a few more bits about SynEdit and Multiframe. Therefore, it does not includes any fancy features as checking if a file has been externally modified, etc.

Attached is a new version of VIPER, incorporating some changes (but it does not yet includes the handy function "CurrentEditor" suggested by lucamar).

Now, could someone tell me why the heck the poNoConsole parameter of the RunCommand function is ignored by R (at least under M$-Windoze, I have not yet tested it under GNU/Linux)?

Cheers,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

 

TinyPortal © 2005-2018