Browse Source

+ fcl-db tests: GUI test framework: add db connector selector/editor. Addresses issue #23483

git-svn-id: trunk@23162 -
reiniero 12 years ago
parent
commit
4820160ed1

+ 2 - 0
.gitattributes

@@ -2135,6 +2135,8 @@ packages/fcl-db/tests/dbftoolsunit.pas svneol=native#text/plain
 packages/fcl-db/tests/dbtestframework.pas svneol=native#text/plain
 packages/fcl-db/tests/dbtestframework_gui.lpi svneol=native#text/plain
 packages/fcl-db/tests/dbtestframework_gui.lpr svneol=native#text/plain
+packages/fcl-db/tests/inieditor.lfm svneol=native#text/plain
+packages/fcl-db/tests/inieditor.pas svneol=native#text/plain
 packages/fcl-db/tests/memdstoolsunit.pas svneol=native#text/plain
 packages/fcl-db/tests/sdfdstoolsunit.pas svneol=native#text/plain
 packages/fcl-db/tests/sqldbtoolsunit.pas svneol=native#text/plain

+ 7 - 4
packages/fcl-db/tests/dbtestframework_gui.lpi

@@ -42,16 +42,19 @@
         <FormatVersion Value="1"/>
       </local>
     </RunParams>
-    <RequiredPackages Count="3">
+    <RequiredPackages Count="4">
       <Item1>
-        <PackageName Value="FPCUnitTestRunner"/>
+        <PackageName Value="SynEdit"/>
       </Item1>
       <Item2>
-        <PackageName Value="LCL"/>
+        <PackageName Value="FPCUnitTestRunner"/>
       </Item2>
       <Item3>
-        <PackageName Value="FCL"/>
+        <PackageName Value="LCL"/>
       </Item3>
+      <Item4>
+        <PackageName Value="FCL"/>
+      </Item4>
     </RequiredPackages>
     <Units Count="1">
       <Unit0>

+ 15 - 1
packages/fcl-db/tests/dbtestframework_gui.lpr

@@ -10,7 +10,9 @@ program dbtestframework_gui;
 // the .ppu files before you can build fcl-db in the regular way. (Using fpmake)
 
 uses
-  Interfaces, Forms, GuiTestRunner,
+  Interfaces, Forms,
+  // GUI:
+  GuiTestRunner, inieditor,
   // Generic DB test framework units
   ToolsUnit,
   // Connectors for different database-types
@@ -30,8 +32,20 @@ uses
 
 {$R *.res}
 
+var
+  DBSelectForm: TFormIniEditor;
 begin
   Application.Initialize;
+  DBSelectForm:=TFormIniEditor.Create(nil);
+  try
+    DBSelectForm.INIFile:='database.ini';
+    DBSelectForm.ProfileSelectSection:='Database';
+    DBSelectForm.ProfileSelectKey:='type';
+    // We can ignore resulting db selection as the file is saved already:
+    DBSelectForm.ShowModal;
+  finally
+    DBSelectForm.Free;
+  end;
   Application.CreateForm(TGuiTestRunner, TestRunner);
   Application.Run;
 end.

+ 579 - 0
packages/fcl-db/tests/inieditor.lfm

@@ -0,0 +1,579 @@
+object FormIniEditor: TFormIniEditor
+  Left = 238
+  Height = 501
+  Top = 145
+  Width = 754
+  Caption = 'Select/edit profile'
+  ClientHeight = 501
+  ClientWidth = 754
+  OnCloseQuery = FormCloseQuery
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  OnDropFiles = FormDropFiles
+  OnShow = FormShow
+  LCLVersion = '1.1'
+  inline SynMemo: TSynMemo
+    Cursor = crIBeam
+    Left = 8
+    Height = 360
+    Top = 88
+    Width = 736
+    Font.Height = -13
+    Font.Name = 'Courier New'
+    Font.Pitch = fpFixed
+    Font.Quality = fqNonAntialiased
+    ParentColor = False
+    ParentFont = False
+    TabOrder = 2
+    Gutter.Width = 57
+    Gutter.MouseActions = <>
+    Highlighter = SynIniHighlighter
+    Keystrokes = <    
+      item
+        Command = ecUp
+        ShortCut = 38
+      end    
+      item
+        Command = ecSelUp
+        ShortCut = 8230
+      end    
+      item
+        Command = ecScrollUp
+        ShortCut = 16422
+      end    
+      item
+        Command = ecDown
+        ShortCut = 40
+      end    
+      item
+        Command = ecSelDown
+        ShortCut = 8232
+      end    
+      item
+        Command = ecScrollDown
+        ShortCut = 16424
+      end    
+      item
+        Command = ecLeft
+        ShortCut = 37
+      end    
+      item
+        Command = ecSelLeft
+        ShortCut = 8229
+      end    
+      item
+        Command = ecWordLeft
+        ShortCut = 16421
+      end    
+      item
+        Command = ecSelWordLeft
+        ShortCut = 24613
+      end    
+      item
+        Command = ecRight
+        ShortCut = 39
+      end    
+      item
+        Command = ecSelRight
+        ShortCut = 8231
+      end    
+      item
+        Command = ecWordRight
+        ShortCut = 16423
+      end    
+      item
+        Command = ecSelWordRight
+        ShortCut = 24615
+      end    
+      item
+        Command = ecPageDown
+        ShortCut = 34
+      end    
+      item
+        Command = ecSelPageDown
+        ShortCut = 8226
+      end    
+      item
+        Command = ecPageBottom
+        ShortCut = 16418
+      end    
+      item
+        Command = ecSelPageBottom
+        ShortCut = 24610
+      end    
+      item
+        Command = ecPageUp
+        ShortCut = 33
+      end    
+      item
+        Command = ecSelPageUp
+        ShortCut = 8225
+      end    
+      item
+        Command = ecPageTop
+        ShortCut = 16417
+      end    
+      item
+        Command = ecSelPageTop
+        ShortCut = 24609
+      end    
+      item
+        Command = ecLineStart
+        ShortCut = 36
+      end    
+      item
+        Command = ecSelLineStart
+        ShortCut = 8228
+      end    
+      item
+        Command = ecEditorTop
+        ShortCut = 16420
+      end    
+      item
+        Command = ecSelEditorTop
+        ShortCut = 24612
+      end    
+      item
+        Command = ecLineEnd
+        ShortCut = 35
+      end    
+      item
+        Command = ecSelLineEnd
+        ShortCut = 8227
+      end    
+      item
+        Command = ecEditorBottom
+        ShortCut = 16419
+      end    
+      item
+        Command = ecSelEditorBottom
+        ShortCut = 24611
+      end    
+      item
+        Command = ecToggleMode
+        ShortCut = 45
+      end    
+      item
+        Command = ecCopy
+        ShortCut = 16429
+      end    
+      item
+        Command = ecPaste
+        ShortCut = 8237
+      end    
+      item
+        Command = ecDeleteChar
+        ShortCut = 46
+      end    
+      item
+        Command = ecCut
+        ShortCut = 8238
+      end    
+      item
+        Command = ecDeleteLastChar
+        ShortCut = 8
+      end    
+      item
+        Command = ecDeleteLastChar
+        ShortCut = 8200
+      end    
+      item
+        Command = ecDeleteLastWord
+        ShortCut = 16392
+      end    
+      item
+        Command = ecUndo
+        ShortCut = 32776
+      end    
+      item
+        Command = ecRedo
+        ShortCut = 40968
+      end    
+      item
+        Command = ecLineBreak
+        ShortCut = 13
+      end    
+      item
+        Command = ecSelectAll
+        ShortCut = 16449
+      end    
+      item
+        Command = ecCopy
+        ShortCut = 16451
+      end    
+      item
+        Command = ecBlockIndent
+        ShortCut = 24649
+      end    
+      item
+        Command = ecLineBreak
+        ShortCut = 16461
+      end    
+      item
+        Command = ecInsertLine
+        ShortCut = 16462
+      end    
+      item
+        Command = ecDeleteWord
+        ShortCut = 16468
+      end    
+      item
+        Command = ecBlockUnindent
+        ShortCut = 24661
+      end    
+      item
+        Command = ecPaste
+        ShortCut = 16470
+      end    
+      item
+        Command = ecCut
+        ShortCut = 16472
+      end    
+      item
+        Command = ecDeleteLine
+        ShortCut = 16473
+      end    
+      item
+        Command = ecDeleteEOL
+        ShortCut = 24665
+      end    
+      item
+        Command = ecUndo
+        ShortCut = 16474
+      end    
+      item
+        Command = ecRedo
+        ShortCut = 24666
+      end    
+      item
+        Command = ecGotoMarker0
+        ShortCut = 16432
+      end    
+      item
+        Command = ecGotoMarker1
+        ShortCut = 16433
+      end    
+      item
+        Command = ecGotoMarker2
+        ShortCut = 16434
+      end    
+      item
+        Command = ecGotoMarker3
+        ShortCut = 16435
+      end    
+      item
+        Command = ecGotoMarker4
+        ShortCut = 16436
+      end    
+      item
+        Command = ecGotoMarker5
+        ShortCut = 16437
+      end    
+      item
+        Command = ecGotoMarker6
+        ShortCut = 16438
+      end    
+      item
+        Command = ecGotoMarker7
+        ShortCut = 16439
+      end    
+      item
+        Command = ecGotoMarker8
+        ShortCut = 16440
+      end    
+      item
+        Command = ecGotoMarker9
+        ShortCut = 16441
+      end    
+      item
+        Command = ecSetMarker0
+        ShortCut = 24624
+      end    
+      item
+        Command = ecSetMarker1
+        ShortCut = 24625
+      end    
+      item
+        Command = ecSetMarker2
+        ShortCut = 24626
+      end    
+      item
+        Command = ecSetMarker3
+        ShortCut = 24627
+      end    
+      item
+        Command = ecSetMarker4
+        ShortCut = 24628
+      end    
+      item
+        Command = ecSetMarker5
+        ShortCut = 24629
+      end    
+      item
+        Command = ecSetMarker6
+        ShortCut = 24630
+      end    
+      item
+        Command = ecSetMarker7
+        ShortCut = 24631
+      end    
+      item
+        Command = ecSetMarker8
+        ShortCut = 24632
+      end    
+      item
+        Command = ecSetMarker9
+        ShortCut = 24633
+      end    
+      item
+        Command = EcFoldLevel1
+        ShortCut = 41009
+      end    
+      item
+        Command = EcFoldLevel2
+        ShortCut = 41010
+      end    
+      item
+        Command = EcFoldLevel1
+        ShortCut = 41011
+      end    
+      item
+        Command = EcFoldLevel1
+        ShortCut = 41012
+      end    
+      item
+        Command = EcFoldLevel1
+        ShortCut = 41013
+      end    
+      item
+        Command = EcFoldLevel6
+        ShortCut = 41014
+      end    
+      item
+        Command = EcFoldLevel7
+        ShortCut = 41015
+      end    
+      item
+        Command = EcFoldLevel8
+        ShortCut = 41016
+      end    
+      item
+        Command = EcFoldLevel9
+        ShortCut = 41017
+      end    
+      item
+        Command = EcFoldLevel0
+        ShortCut = 41008
+      end    
+      item
+        Command = EcFoldCurrent
+        ShortCut = 41005
+      end    
+      item
+        Command = EcUnFoldCurrent
+        ShortCut = 41003
+      end    
+      item
+        Command = EcToggleMarkupWord
+        ShortCut = 32845
+      end    
+      item
+        Command = ecNormalSelect
+        ShortCut = 24654
+      end    
+      item
+        Command = ecColumnSelect
+        ShortCut = 24643
+      end    
+      item
+        Command = ecLineSelect
+        ShortCut = 24652
+      end    
+      item
+        Command = ecTab
+        ShortCut = 9
+      end    
+      item
+        Command = ecShiftTab
+        ShortCut = 8201
+      end    
+      item
+        Command = ecMatchBracket
+        ShortCut = 24642
+      end    
+      item
+        Command = ecColSelUp
+        ShortCut = 40998
+      end    
+      item
+        Command = ecColSelDown
+        ShortCut = 41000
+      end    
+      item
+        Command = ecColSelLeft
+        ShortCut = 40997
+      end    
+      item
+        Command = ecColSelRight
+        ShortCut = 40999
+      end    
+      item
+        Command = ecColSelPageDown
+        ShortCut = 40994
+      end    
+      item
+        Command = ecColSelPageBottom
+        ShortCut = 57378
+      end    
+      item
+        Command = ecColSelPageUp
+        ShortCut = 40993
+      end    
+      item
+        Command = ecColSelPageTop
+        ShortCut = 57377
+      end    
+      item
+        Command = ecColSelLineStart
+        ShortCut = 40996
+      end    
+      item
+        Command = ecColSelLineEnd
+        ShortCut = 40995
+      end    
+      item
+        Command = ecColSelEditorTop
+        ShortCut = 57380
+      end    
+      item
+        Command = ecColSelEditorBottom
+        ShortCut = 57379
+      end>
+    MouseActions = <>
+    MouseSelActions = <>
+    Lines.Strings = (
+      ''
+    )
+    SelectedColor.BackPriority = 50
+    SelectedColor.ForePriority = 50
+    SelectedColor.FramePriority = 50
+    SelectedColor.BoldPriority = 50
+    SelectedColor.ItalicPriority = 50
+    SelectedColor.UnderlinePriority = 50
+    OnStatusChange = SynMemoStatusChange
+    inline SynLeftGutterPartList1: TSynGutterPartList
+      object SynGutterMarks1: TSynGutterMarks
+        Width = 24
+        MouseActions = <>
+      end
+      object SynGutterLineNumber1: TSynGutterLineNumber
+        Width = 17
+        MouseActions = <>
+        MarkupInfo.Background = clBtnFace
+        MarkupInfo.Foreground = clNone
+        DigitCount = 2
+        ShowOnlyLineNumbersMultiplesOf = 1
+        ZeroStart = False
+        LeadingZeros = False
+      end
+      object SynGutterChanges1: TSynGutterChanges
+        Width = 4
+        MouseActions = <>
+        ModifiedColor = 59900
+        SavedColor = clGreen
+      end
+      object SynGutterSeparator1: TSynGutterSeparator
+        Width = 2
+        MouseActions = <>
+      end
+      object SynGutterCodeFolding1: TSynGutterCodeFolding
+        MouseActions = <>
+        MarkupInfo.Background = clNone
+        MarkupInfo.Foreground = clGray
+        MouseActionsExpanded = <>
+        MouseActionsCollapsed = <>
+      end
+    end
+  end
+  object FileNameEdit: TFileNameEdit
+    Left = 56
+    Height = 23
+    Top = 24
+    Width = 368
+    OnAcceptFileName = FileNameEditAcceptFileName
+    FilterIndex = 0
+    HideDirectories = False
+    ButtonWidth = 23
+    NumGlyphs = 1
+    MaxLength = 0
+    TabOrder = 0
+  end
+  object INIFileLabel: TLabel
+    Left = 8
+    Height = 15
+    Top = 24
+    Width = 34
+    Caption = 'INI file'
+    ParentColor = False
+  end
+  object ProfileSelect: TComboBox
+    Left = 56
+    Height = 23
+    Hint = 'Choose the profile you want to enable'
+    Top = 61
+    Width = 164
+    ItemHeight = 15
+    OnSelect = ProfileSelectSelect
+    Sorted = True
+    TabOrder = 1
+  end
+  object ProfileLabel: TLabel
+    Left = 8
+    Height = 15
+    Top = 64
+    Width = 34
+    Caption = 'Profile'
+    ParentColor = False
+  end
+  object OKButton: TButton
+    Left = 664
+    Height = 25
+    Top = 456
+    Width = 75
+    Caption = '&OK'
+    Default = True
+    ModalResult = 1
+    TabOrder = 4
+  end
+  object CancelButton: TButton
+    Left = 568
+    Height = 25
+    Top = 456
+    Width = 75
+    Cancel = True
+    Caption = '&Cancel'
+    ModalResult = 2
+    OnClick = CancelButtonClick
+    TabOrder = 3
+  end
+  object SynIniHighlighter: TSynIniSyn
+    DefaultFilter = 'INI Files (*.ini)|*.ini'
+    Enabled = False
+    CommentAttri.FrameEdges = sfeAround
+    TextAttri.FrameEdges = sfeAround
+    SectionAttri.FrameEdges = sfeAround
+    KeyAttri.FrameEdges = sfeAround
+    NumberAttri.FrameEdges = sfeAround
+    SpaceAttri.FrameEdges = sfeAround
+    StringAttri.FrameEdges = sfeAround
+    SymbolAttri.FrameEdges = sfeAround
+    left = 32
+    top = 456
+  end
+  object GUITimer: TIdleTimer
+    AutoEnabled = True
+    OnTimer = GUITimerTimer
+    left = 560
+    top = 16
+  end
+end

+ 251 - 0
packages/fcl-db/tests/inieditor.pas

@@ -0,0 +1,251 @@
+unit inieditor;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls,
+  Graphics, Dialogs, StdCtrls, EditBtn, ExtCtrls,
+  IniFiles, lazutf8, SynEdit,SynMemo,{SynEditTypes,}SynHighlighterIni, SynEditTypes;
+
+type
+
+  { TFormIniEditor }
+
+  TFormIniEditor = class(TForm)
+    GUITimer: TIdleTimer;
+    OKButton: TButton;
+    CancelButton: TButton;
+    ProfileSelect: TComboBox;
+    FileNameEdit: TFileNameEdit;
+    INIFileLabel: TLabel;
+    ProfileLabel: TLabel;
+    SynIniHighlighter: TSynIniSyn;
+    SynMemo: TSynMemo;
+    procedure CancelButtonClick(Sender: TObject);
+
+    procedure FileNameEditAcceptFileName(Sender: TObject; var Value: String);
+    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
+    procedure FormShow(Sender: TObject);
+    procedure GUITimerTimer(Sender: TObject);
+    procedure ProfileSelectSelect(Sender: TObject);
+    procedure SynMemoStatusChange(Sender: TObject; Changes: TSynStatusChanges);
+  private
+    FIniFilename: string;
+    FIniStream: TMemoryStream;
+    FMustReloadProfileSelect: boolean;
+    FSaveChanges: boolean; //If off, don't save any changes (e.g. when canceling form)
+    FProfileSelectKey: string;
+    FProfileSelectSection: string;
+    FProfileSelectValue: string;
+    procedure LoadGUI(KeepCursorPosition: boolean; LoadSynmemo: boolean; LoadProfileselect: boolean); //Loads GUI elements from backing stream
+    procedure SetIniFilename(AValue: string); //Loads file into stream
+    procedure SetProfileSelectKey(AValue: string);
+    procedure SetProfileSelectSection(AValue: string);
+    // Saves synmemo contents to memory stream and optionally to file, too
+    procedure SynMemoSave(AlsoToFile: boolean);
+    { private declarations }
+  public
+    // File to be edited/viewed:
+    property INIFile: string read FIniFilename write SetIniFilename;
+    // Section name where profile selection is to be stored. If nothing, the root/top level will be used
+    property ProfileSelectSection: string read FProfileSelectSection write SetProfileSelectSection;
+    // Key to be used for storing selected profile
+    // If empty, don't use the select profile combobox
+    property ProfileSelectKey: string read FProfileSelectKey write SetProfileSelectKey;
+    // The value the user selected
+    property ProfileSelectValue: string read FProfileSelectValue;
+  end;
+
+var
+  Form1: TFormIniEditor;
+
+implementation
+
+{$R *.lfm}
+
+{ TFormIniEditor }
+
+
+procedure TFormIniEditor.FormDropFiles(Sender: TObject;
+  const FileNames: array of String);
+begin
+  // Go through property for reload file+reload GUI code
+  IniFile:=FileNames[0];
+end;
+
+procedure TFormIniEditor.FormShow(Sender: TObject);
+begin
+  if FileNameEdit.FileName='' then
+    FileNameEdit.FileName:=ExpandFileNameUTF8(FIniFilename);
+end;
+
+procedure TFormIniEditor.GUITimerTimer(Sender: TObject);
+begin
+  if FMustReloadProfileSelect then
+    LoadGUI(true,false,true);
+end;
+
+procedure TFormIniEditor.ProfileSelectSelect(Sender: TObject);
+// Save selected profile
+var
+  MyIniFile: TIniFile;
+begin
+  FProfileSelectValue:=ProfileSelect.Text;
+  if FProfileSelectValue<>'' then
+  begin
+    // Save any changes in synmemo
+    SynMemoSave(false);
+    FIniStream.Position:=0; //Load from beginning
+    MyIniFile := TIniFile.Create(FIniStream, True);
+    FIniStream.Position:=0; //Needed to save again
+    try
+      MyIniFile.WriteString(FProfileSelectSection,FProfileSelectKey,FProfileSelectValue);
+    finally
+      MyIniFile.Free;
+    end;
+    LoadGUI(true,true,true);
+  end;
+end;
+
+procedure TFormIniEditor.SynMemoStatusChange(Sender: TObject;
+  Changes: TSynStatusChanges);
+begin
+  // If user edits the profile name directly or edits/deletes/adds
+  // one of the section names, the combobox will need to be refreshed.
+  // Pass this on to a timer so it gets done without disturbing the updates
+  if (FMustReloadProfileSelect=false) and (scModified in Changes) then
+  begin
+    //todo: test for section line or key=value
+    FMustReloadProfileSelect:=true;
+  end;
+end;
+
+procedure TFormIniEditor.LoadGUI(KeepCursorPosition: boolean; LoadSynmemo: boolean; LoadProfileselect: boolean);
+var
+  MyIniFile : TIniFile;
+  OldPos: TPoint;
+  ProfIndex: integer;
+  Sections: TStringList;
+begin
+  SynMemo.BeginUpdate(false);
+  if LoadSynMemo then
+  begin
+    OldPos:=SynMemo.CaretXY;
+    FIniStream.Position:=0; //Load from beginning
+    SynMemo.Lines.LoadFromStream(FIniStream);
+  end;
+  FIniStream.Position:=0; //Load from beginning
+  MyIniFile := TIniFile.Create(FIniStream, True);
+  FIniStream.Position:=0; //In case we want to save
+  Sections:=TStringList.Create;
+  try
+    if LoadProfileselect then
+    begin
+      Sections.Sorted:=true;
+      MyIniFile.ReadSections(Sections);
+      // Now take out the one where the profile is stored as it will confuse users
+      if Sections.Find(FProfileSelectSection, ProfIndex) then
+        Sections.Delete(ProfIndex);
+      ProfileSelect.Clear;
+      ProfileSelect.Items.AddStrings(Sections);
+      FProfileSelectValue:=MyIniFile.ReadString(FProfileSelectSection,FProfileSelectKey,'');
+      ProfileSelect.Text:=FProfileSelectValue;
+      FMustReloadProfileSelect:=false;
+    end;
+
+    // restore cursor/caret position
+    if LoadSynMemo and KeepCursorPosition then
+      SynMemo.CaretXY:=OldPos;
+  finally
+    MyIniFile.Free;
+    Sections.Free;
+    SynMemo.EndUpdate;
+  end;
+end;
+
+procedure TFormIniEditor.SetIniFilename(AValue: string);
+begin
+  if FIniFilename=AValue then Exit;
+  FIniFilename:=AValue;
+  if FIniFileName<>'' then
+  begin
+    try
+      FIniStream.Clear;
+      FIniStream.LoadFromFile(FIniFilename);
+    except
+      on E: Exception do
+        ShowMessage('Error loading file '+FIniFilename+': '+E.Message);
+    end;
+    LoadGUI(false,true,true);
+  end;
+end;
+
+procedure TFormIniEditor.SetProfileSelectKey(AValue: string);
+begin
+  if FProfileSelectKey=AValue then Exit;
+  FProfileSelectKey:=AValue;
+  if (FProfileSelectKey<>'') and (FProfileSelectSection<>'') then
+    LoadGUI(false,true,true);
+end;
+
+procedure TFormIniEditor.SetProfileSelectSection(AValue: string);
+begin
+  if FProfileSelectSection=AValue then Exit;
+  FProfileSelectSection:=AValue;
+  if (FProfileSelectKey<>'') and (FProfileSelectSection<>'') then
+    LoadGUI(false,true,true);
+end;
+
+procedure TFormIniEditor.SynMemoSave(AlsoToFile: boolean);
+begin
+  if FSaveChanges then
+  begin
+    FIniStream.Clear;
+    FIniStream.Position:=0; //Save from beginning
+    SynMemo.Lines.SaveToStream(FiniStream);
+    FIniStream.Position:=0; //Save from beginning
+    if AlsoToFile and (FIniFileName<>'') then
+      FIniStream.SaveToFile(FIniFilename);
+  end;
+end;
+
+
+procedure TFormIniEditor.CancelButtonClick(Sender: TObject);
+begin
+  FSaveChanges:=false;
+end;
+
+
+
+procedure TFormIniEditor.FileNameEditAcceptFileName(Sender: TObject;
+  var Value: String);
+begin
+  // go through the property to load new file
+  INIFile:=Value;
+end;
+
+procedure TFormIniEditor.FormCloseQuery(Sender: TObject; var CanClose: boolean);
+begin
+  // Save any uncommitted changes
+  //todo: only if dirty synmemo changestamp?; also check if this is best event
+  SynMemoSave(true);
+end;
+
+procedure TFormIniEditor.FormCreate(Sender: TObject);
+begin
+  FIniStream:=TMemoryStream.Create;
+  FSaveChanges:=true; //allow editing
+end;
+
+procedure TFormIniEditor.FormDestroy(Sender: TObject);
+begin
+  FIniStream.Free;
+end;
+
+end.
+