{*********************************************************}
{*                  TXTEDIT1.PAS 1.03                    *}
{*    Copyright (c) TurboPower Software Co 1995,1996     *}
{*                 All rights reserved.                  *}
{*********************************************************}

{******************************************************************************
*
*  Copyright © POV-Team(tm) 1996. All Rights Reserved.
*  This Editor DLL code is Copyright 1996 Christopher J. Cason.
*  Author : Christopher J. Cason.
*           Based on Turbopower Software's textedit example.
*
*  from Persistence of Vision Raytracer(tm)
*  Copyright 1996 Persistence of Vision Team
*
* The terms POV-Ray, POV, and Persistence of Vision Raytracer are trademarks
* of the Persistence of Vision Team.
*---------------------------------------------------------------------------
*  NOTICE: This source code file is provided so that users may experiment
*  with enhancements to POV-Ray(tm) and to port the software to platforms other
*  than those supported by the POV-Ray Team.  There are strict rules under
*  which you are permitted to use this file.  The rules are in the file
*  named POVLEGAL.DOC which should be distributed with this file. If
*  POVLEGAL.DOC is not available or for more info please contact the POV-Ray(tm)
*  Team Coordinator by leaving a message in CompuServe's POVRAY Forum.
*  The latest version of POV-Ray may be found there as well.
*  POV-Ray files may also be found on the world wide web at www.povray.org.
*
* POV-Ray is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*******************************************************************************}

// This code is not commented enough or as much as I'd like. It also needs
// a major cleanup. However, after all, this is volunteer work and I've
// not got the time to do so now. Rather than further delay the release,
// here it is.

unit ClassTPovEdit ;

interface

uses
  SysUtils,
  WinTypes,
  WinProcs,
  Messages,
  Classes,
  Graphics,
  Controls,
  CommCtrl,
  Forms,
  Dialogs,
  ExtCtrls,
  OvcBase,
  OvcData,
  Menus,
  FileCtrl,
  Printers,
  OvcEdit,
  ShellAPI,
  inifiles,
  OvcEditU,
  OvcConst,
  ClassTFindDlg,
  ClassTReplDlg,
  ClassTStatusForm ;

const
  EditorCount = 16 ;
  CloseEditorMessage = WM_USER + 1010 ;
  CreateEditorMessage = WM_USER + 1015 ;
  EditorRenderMessage = WM_USER + 1020 ;
  ShowMessagesMessage = WM_USER + 1025 ;
  CM_FILEEXIT = 2040 ;
  CM_DUMPPANE = 2035 ;
  CM_CLEARMESSAGES = 2200 ;

type
  {---------------------------------------------------------------------------}
  { TextPtr is a list of pchars.  TextList is a list of TextPtr lists.  Both  }
  { lists are singly-linked, since there's no need for complicated list       }
  { traversal. We use TextList to store the templates that the Insert Menu is }
  { linking to...  Each TextList node corresponds to an individual template.  }
  { Each TextPtr node corresponds to a line of text in the template.          }
  {---------------------------------------------------------------------------}

  TextPtr = ^TextRecord ;
  TextRecord = Record
    TextLine : Pchar ;
    NextLine : TextPtr ;
  end ;

  TextList = ^TextListRecord ;
  TextListRecord = Record
    ItemNumber : Longint ;
    FirstLine  : TextPtr ;
    LastLine   : textPtr ;
    NextItem   : TextList ;
  end ;

  TPovEdit = class (TForm)
      FileMenu            : TMenuItem ;
      PrintSetup1         : TMenuItem ;
      Print1              : TMenuItem ;
      N2                  : TMenuItem ;
      EditMenu            : TMenuItem ;
      Copy1               : TMenuItem ;
      SearchMenu          : TMenuItem ;
      GotoLine1           : TMenuItem ;
      N3                  : TMenuItem ;
      FindNext1           : TMenuItem ;
      Find1               : TMenuItem ;
      OptionsMenu         : TMenuItem ;
      Tabs1               : TMenuItem ;
      N5                  : TMenuItem ;
      HelpMenu            : TMenuItem ;
      N6                  : TMenuItem ;
      Redo1               : TMenuItem ;
      Undo1               : TMenuItem ;
      Undo2               : TMenuItem ;
      Cut1                : TMenuItem ;
      Paste1              : TMenuItem ;
      Delete1             : TMenuItem ;
      Replace1            : TMenuItem ;
      AutoIndentation1    : TMenuItem ;
      CreateBackupFile1   : TMenuItem ;
      FixedTabs1          : TMenuItem ;
      RealTabs1           : TMenuItem ;
      SmartTabs1          : TMenuItem ;
      N7                  : TMenuItem ;
      TabSize1            : TMenuItem ;
      Redo2               : TMenuItem ;
      InsertMenu          : TMenuItem ;
      FileBreak           : TMenuItem ;
      POVRayContextHelp   : TMenuItem ;
      N8                  : TMenuItem ;
      SelectAll1          : TMenuItem ;
      N1                  : TMenuItem ;
      OpenInCurrent       : TMenuItem ;
      OpenInNew           : TMenuItem ;
      NewFile             : TMenuItem ;
      CloseWindow         : TMenuItem ;
      CloseAll            : TMenuItem ;
      Save                : TMenuItem ;
      SaveAs              : TMenuItem ;
      Exit1               : TMenuItem ;
      CopyPane            : TMenuItem ;
      NewFile1            : TMenuItem ;
      Close1              : TMenuItem ;
      Save1               : TMenuItem ;
      Saveas1             : TMenuItem ;
      Find2               : TMenuItem ;
      Replace2            : TMenuItem ;
      SelectAll2          : TMenuItem ;
      N9                  : TMenuItem ;
      N10                 : TMenuItem ;
      POVRayContextHelp1  : TMenuItem ;
      N12                 : TMenuItem ;
      RenderOptions1      : TMenuItem ;
      Render1             : TMenuItem ;
      AppearanceMenu      : TMenuItem ;
      Font2               : TMenuItem ;
      PopupMenu           : TPopupMenu ;
      PopupMenu1          : TPopupMenu ;
      SaveDialog1         : TSaveDialog ;
      FontDialog1         : TFontDialog ;
      OpenDialog1         : TOpenDialog ;
      PrintDialog1        : TPrintDialog ;
      DefaultController   : TOvcController;
      Editor1             : TOvcTextFileEditor ;
      Editor2             : TOvcTextFileEditor ;
      Editor3             : TOvcTextFileEditor ;
      Editor4             : TOvcTextFileEditor ;
      Editor5             : TOvcTextFileEditor ;
      Editor6             : TOvcTextFileEditor ;
      Editor7             : TOvcTextFileEditor ;
      Editor8             : TOvcTextFileEditor ;
      Editor9             : TOvcTextFileEditor ;
      Editor10            : TOvcTextFileEditor ;
      Editor11            : TOvcTextFileEditor ;
      Editor12            : TOvcTextFileEditor ;
      Editor13            : TOvcTextFileEditor ;
      Editor14            : TOvcTextFileEditor ;
      Editor15            : TOvcTextFileEditor ;
      Editor16            : TOvcTextFileEditor ;
      PrinterSetupDialog1 : TPrinterSetupDialog ;
    N13: TMenuItem;
    N14: TMenuItem;
    WordStarCommands1: TMenuItem;
    DividerPanel: TPanel;
    N4: TMenuItem;
    ToggleMessageDisplay1: TMenuItem;
    SaveAll: TMenuItem;

      procedure Font1Click (Sender : TObject) ;
      procedure OpenInCurrentClick (Sender : TObject) ;
      procedure PrintSetup1Click (Sender : TObject) ;
      procedure Print1Click (Sender : TObject) ;
      procedure SelectAll1Click (Sender : TObject) ;
      procedure Edit1Click (Sender : TObject) ;
      procedure FileMenuClick (Sender : TObject) ;
      procedure CopyClick (Sender : TObject) ;
      procedure Search1Click (Sender : TObject) ;
      procedure GotoLine1Click (Sender : TObject) ;
      procedure TabSize1Click (Sender : TObject) ;
      procedure Find1Click (Sender : TObject) ;
      procedure FindNext1Click (Sender : TObject) ;
      procedure WordStarCommands1Click (Sender : TObject) ;
      procedure EditorShowStatus (Sender : TObject ; LineNum : Longint ; ColNum : Integer) ;
      procedure AutoIndentation1Click (Sender : TObject) ;
      procedure CreateBackupFile1Click (Sender : TObject) ;
      procedure TabTypeClick (Sender : TObject) ;
      procedure NewFileClick (Sender : TObject) ;
      procedure SaveAsClick (Sender : TObject) ;
      procedure FormCreate (Sender : TObject) ;
      procedure SaveClick (Sender : TObject) ;
      procedure UndoClick (Sender : TObject) ;
      procedure RedoClick (Sender : TObject) ;
      procedure CutClick (Sender : TObject) ;
      procedure PasteClick (Sender : TObject) ;
      procedure Delete1Click (Sender : TObject) ;
      procedure FormCloseQuery (Sender : TObject ; var CanClose : Boolean) ;
      procedure InsertMenuClicked (Sender : TObject) ;
      procedure ListOfTemplates ;
      procedure FormClose (Sender : TObject ; var Action : TCloseAction) ;
      procedure ReopenFile (Sender : TObject) ;
      procedure UpdateFileMenu ;
      procedure POVRayContextHelpClick (Sender : TObject) ;
      procedure Replace1Click (Sender : TObject) ;
      procedure CloseWindowClick (Sender : TObject) ;
      procedure Exit1Click (Sender : TObject) ;
      procedure NewWindowClick (Sender : TObject) ;
      procedure OpenInNewClick (Sender : TObject) ;
      procedure CloseAllClick (Sender : TObject) ;
      procedure CopyPaneClick (Sender : TObject) ;
      procedure ClearMessagesClick (Sender : TObject) ;
      procedure OnPopup (Sender : TObject) ;
      procedure OnUserCommand (Sender : TObject ; Command : Word) ;
      procedure Render1Click (Sender : TObject) ;
      procedure RenderOptions1Click (Sender : TObject) ;
      procedure FormResize (Sender : TObject) ;
      procedure ToggleMessageDisplay1Click (Sender : TObject) ;
      procedure EditorError (Sender : TObject ; var ErrorCode : Word) ;
      procedure SaveAllClick (Sender : TObject) ;

    private
      IniFile       : TIniFile ;
      FirstTemplate : TextList ;
      LastTemplate  : TextList ;
      NumberOfFiles : Longint ;
      EditorActive  : Boolean ;

      procedure CreateParams (var Params : TCreateParams) ; override ;
      function GetEditor (Index : Integer) : TOvcTextFileEditor ;
      function FindTab (Editor : TOvcTextFileEditor) : Integer ;
      procedure WndProc (var Message : TMessage) ; override ;

    public
      Home          : String ;
      InitFileName  : String ;
      MainWindow    : THandle ;
      TabbedWindow  : THandle ;
      ParentWindow  : THandle ;
      InitLineNum   : Integer ;
      FindDlg       : TFindDlg ;
      ReplDlg       : TReplDlg ;
      HistoryList   : TStringList ;
      Editor        : TOvcTextFileEditor ;

      property Editors [Index : Integer] : TOvcTextFileEditor read GetEditor ;

      constructor Create (hWndMain : THandle ; hWndTabbed : THandle ; HomePath : PChar) ;

      procedure UpdateTab ;
      procedure UpdateHistory ;
      function FileIsLoaded (SourceEditor : TOvcTextFileEditor ; FileName : String) : TOvcTextFileEditor ;
      function GetNextEditor : TOvcTextFileEditor ;
      function ReleaseEditor (TheEditor : TOvcTextFileEditor) : boolean ;
      function ProgramCloseQuery : Boolean ;
      procedure SetMenuActive (Active : Boolean) ;
      procedure WriteSettings ;
  end ;

var
  PovEdit : TPovEdit ;

implementation

{$R *.DFM}

procedure PovShowMessage (Str : String) ;
begin
  Application.MessageBox (PChar (Str), PChar ('POV-Ray Editor'), MB_ICONEXCLAMATION) ;
end ;

constructor TPovEdit.Create (hWndMain : THandle ; hWndTabbed : THandle ; HomePath : PChar) ;
begin
  MainWindow := hWndMain ;
  TabbedWindow := hWndTabbed ;
  ParentWindow := hWndTabbed ;
  Home := String (HomePath) ;
  inherited Create (Application) ;
  FindDlg := TFindDlg.Create (Self) ;
  ReplDlg := TReplDlg.Create (Self) ;
  StatusForm := TStatusForm.Create (hWndTabbed) ;
  StatusForm.Left := Left ;
  StatusForm.Width := Width ;
  StatusForm.Height := StatusForm.Panel1.Height ;
  Hide ;
end ;

procedure TPovEdit.UpdateHistory ;
var
  FileCtr : Integer ;
begin
  // if the history list already has 9 entries in it, then delete the last entry in the list.
  If HistoryList.Count = 9 then
    HistoryList.Delete (8) ;

  try
    // clear the history list from the ini file.
    For FileCtr := 1 to 9 do
      IniFile.WriteString ('Editor', 'Recent' + IntToStr (FileCtr), '') ;

    // flush the history list into the ini file.
    if HistoryList.Count > 0 then
      For FileCtr := 0 to HistoryList.Count - 1 do
        IniFile.WriteString ('Editor', 'Recent' + IntToStr (FileCtr + 1), HistoryList [FileCtr]) ;
  except
    on E : Exception do
    begin
      PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
    end ;
  end ;

  // insert the history list into the file menu.
  UpdateFileMenu ;
end ;

procedure TPovEdit.Font1Click (Sender : TObject) ;
var
  I : Integer ;
begin
  FontDialog1.Font.Assign (Editor.FixedFont.Font) ;
  if FontDialog1.Execute then
    for I := 1 to EditorCount do
      Editors [I].FixedFont.Assign (FontDialog1.Font) ;
end ;

procedure TPovEdit.UpdateFileMenu ;
var
  HCount, FileIndex, NumberAdded : Integer ;
  DoneWithFiles                  : Boolean ;
  TempMenu                       : TMenuItem ;
begin
  FileIndex := FileMenu.IndexOf (FileBreak) + 1 ;

  DoneWithFiles := False ;
  repeat
    if FileMenu.Items [FileIndex].Caption = '-' then
      DoneWithFiles := True
    else
      FileMenu.Delete (FileIndex) ;
  until DoneWithFiles ;

  NumberAdded := 0 ;
  if HistoryList.Count > 0 then
  begin
    for HCount := 0 to HistoryList.Count - 1 do
    begin
      if FileIsLoaded (nil, HistoryList [HCount]) = nil then
      begin
        TempMenu := TMenuItem.Create (Self) ;
        TempMenu.Caption := '&' + IntToStr (NumberAdded + 1) + ' ' + ExtractFileName (HistoryList [HCount]) + #0 + IntToStr (HCount) ;
        TempMenu.OnClick := ReOpenFile ;
        FileMenu.Insert (FileIndex + NumberAdded, TempMenu) ;
        Inc (NumberAdded) ;
      end ;
    end ;
  end ;
  FileBreak.Visible := (NumberAdded > 0) ;
end ;

procedure TPovEdit.OpenInCurrentClick (Sender : TObject) ;
var
  Search    : TSearchRec ;
  Result    : Integer ;
  Path      : String ;
  OkToClose : Boolean ;
  OpenIn    : TOvcTextFileEditor ;
begin
  if not EditorActive then
    SendMessage (MainWindow, CreateEditorMessage, 0, 0) ;
  FormCloseQuery (Self, OkToClose) ;
  if not (OkToClose) then
    exit ;
  Path := IniFile.ReadString ('Editor', 'LastPath', '') ;
  if Path <> '' then
    OpenDialog1.InitialDir := Path ;
  if OpenDialog1.Execute then
  begin
    // extract file path from filename because findfile doesn't include a path.
    Path := ExtractFilePath (OpenDialog1.FileName) ;
    try
      IniFile.WriteString ('Editor', 'LastPath', Path) ;
    except
      on E : Exception do
      begin
        PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
      end ;
    end ;

    // because open dialog might not match the case of the actual file, we grab the filename of the file...
    Result := FindFirst (OpenDialog1.Filename, faAnyFile, Search) ;
    if Result = 0 then
    begin
      OpenIn := FileIsLoaded (Editor, Path + Search.Name) ;
      if OpenIn = nil then
      begin
        Editor.FileName := Path + Search.Name ;
        Editor.IsOpen := True ;
        HistoryList.Add (Editor.FileName) ;
      end
      else
        Editor.Attach (OpenIn) ;
      UpdateTab ;
      UpdateHistory ;
    end ;
    SysUtils.FindClose (Search) ;
  end ;
end ;

procedure TPovEdit.PrintSetup1Click (Sender : TObject) ;
begin
  PrinterSetupDialog1.Execute ;
end ;

procedure TPovEdit.Print1Click (Sender : TObject) ;
var
  StartCol, StopCol   : Integer ;
  Line                : LongInt ;
  StartLine, StopLine : LongInt ;
  PrintText           : System.Text ;
  Buf                 : array [0..1023] of Char ;
begin
  if Editor.GetSelection (StartLine, StartCol, StopLine, StopCol) then
    PrintDialog1.Options := PrintDialog1.Options + [poSelection]
  else
    PrintDialog1.Options := PrintDialog1.Options - [poSelection] ;

  if PrintDialog1.Execute then
  begin
    Screen.Cursor := crHourGlass ;
    AssignPrn (PrintText) ;
    try
      Rewrite (PrintText) ;
      case PrintDialog1.PrintRange of
        prAllPages :
          for Line := 1 to Editor.LineCount do
          begin
            Editor.GetPrintableLine (Line, Buf, SizeOf (Buf)) ;
            Writeln (PrintText, Buf) ;
          end ;

        prSelection :
          for Line := StartLine to StopLine do
          begin
            Editor.GetPrintableLine (Line, Buf, SizeOf (Buf)) ;
            if Line = StartLine then
              FillChar (Buf, StartCol, #32) ;
            if Line = StopLine then
              Buf [StopCol] := #0 ;
            Writeln (PrintText, Buf) ;
          end ;
      end ;
    finally
      System.Close (PrintText) ;
      Screen.Cursor := crDefault ;
    end ;
  end ;
end ;

procedure TPovEdit.SelectAll1Click (Sender : TObject) ;
begin
  if Editor.LineCount = -1 then
    Exit ;
  Editor.SelectAll (False) ;
end ;

procedure TPovEdit.ReOpenFile (Sender : TObject) ;
var
  TempStr     : String ;
  TPos        : Integer ;
  IndexNumber : Integer ;
  OkToClose   : Boolean ;
  TempMenu    : TMenuItem ;
  OpenIn      : TOvcTextFileEditor ;
begin
  SendMessage (MainWindow, CreateEditorMessage, 0, 0) ;
  FormCloseQuery (Self, OkToClose) ;
  if not (OkToClose) then
    exit ;
  TempMenu := TMenuItem (Sender) ;
  TPos := Pos (#0, TempMenu.Caption) ;
  if TPos > 0 then
  begin
    if TPos < Length (TempMenu.Caption) then
    begin
      TempStr := copy (TempMenu.Caption, TPos + 1, Length (TempMenu.Caption) - TPos) ;
      IndexNumber := StrToInt (TempStr) ;
      TempStr := HistoryList [IndexNumber] ;
      HistoryList.Delete (IndexNumber) ;
      if FileExists (TempStr) then
      begin
        HistoryList.Insert (0, TempStr) ;
        OpenIn := FileIsLoaded (Editor, TempStr) ;
        if OpenIn = nil then
        begin
          Editor.FileName := TempStr ;
          Editor.IsOpen := True ;
        end
        else
          Editor.Attach (OpenIn) ;
        UpdateTab ;
        try
          IniFile.WriteString ('Editor', 'LastPath', ExtractFilePath (Editor.FileName)) ;
        except
          on E : Exception do
          begin
            PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
          end ;
        end ;
      end
      else
        PovShowMessage ('Error : This file no longer exists.') ;
      UpdateHistory ;
    end ;
  end ;
end ;

procedure TPovEdit.OnPopup (Sender : TObject) ;
begin
  FileMenuClick (Sender) ;
  Search1Click (Sender) ;
  if SendMessage (MainWindow, ShowMessagesMessage, 1, 0) = 0 then
    ToggleMessageDisplay1.Caption := 'Show &Messages'
  else
    ToggleMessageDisplay1.Caption := 'Hide &Messages' ;
end ;

procedure TPovEdit.FileMenuClick (Sender : TObject) ;
begin
  Print1.Enabled := EditorActive and (Editor.TextLength > 0) ;
  Save.Enabled := EditorActive and (Editor.TextLength > 0) ;
  Save1.Enabled := EditorActive and (Editor.TextLength > 0) ;
  SaveAs.Enabled := EditorActive ;
  SaveAs1.Enabled := EditorActive ;
end ;

procedure TPovEdit.CopyClick (Sender : TObject) ;
begin
  Editor.CopyToClipboard ;
end ;

procedure TPovEdit.Search1Click (Sender : TObject) ;
begin
  Find1.Enabled := Editor.TextLength > 0 ;
  Find2.Enabled := Editor.TextLength > 0 ;
  Replace1.Enabled := Editor.TextLength > 0 ;
  Replace2.Enabled := Editor.TextLength > 0 ;
  FindNext1.Enabled := (Editor.TextLength > 0) and (FindDlg.Edit1.Text <> '') ;
  GoToLine1.Enabled := Editor.TextLength > 0 ;
end ;

procedure TPovEdit.GotoLine1Click (Sender : TObject) ;
var
  S : String ;
begin
  S := '1' ;
  if InputQuery ('Go to Line Number', 'Enter Line Number (1 to ' + IntToStr (Editor.LineCount) + ')', S) then
  try
    Editor.SetCaretPosition (StrToInt (S), 1) ;
    if Editor.CanFocus then
      Editor.SetFocus ;
  except
    Exit ;
  end ;
end ;

procedure TPovEdit.TabSize1Click (Sender : TObject) ;
var
  S : String ;
  TabSize : Integer ;
  I : Integer ;
begin
  S := IntToStr (Editor.TabSize) ;
  if InputQuery ('Tab Size', 'Enter Tab Size', S) then
  try
    TabSize := StrToInt (S) ;
    for I := 1 to EditorCount do
      Editors [I].TabSize := TabSize ;
  except
    Exit ;
  end ;
end ;

procedure TPovEdit.Find1Click (Sender : TObject) ;
var
  OptionSet : TSearchOptionSet ;
begin
  FindDlg.ActiveControl := FindDlg.Edit1 ;
  FindDlg.ShowModal ;
  if FindDlg.ModalResult = MrOK then
  begin
    OptionSet := [] ;
    if FindDlg.RadioButton2.Checked then
      OptionSet := OptionSet + [soBackward] ;
    if FindDlg.Checkbox1.Checked then
      OptionSet := OptionSet + [soMatchCase] ;
    if FindDlg.Checkbox2.Checked then
      OptionSet := OptionSet + [soGlobal] ;
    if not Editor.Search (FindDlg.Edit1.Text, OptionSet) then
      MessageDlg ('Search String ''' + FindDlg.Edit1.Text + ''' not found', MtInformation, [MbOk], 0) ;
  end ;
  if Editor.CanFocus then
    Editor.SetFocus ;
end ;

procedure TPovEdit.FindNext1Click (Sender : TObject) ;
var
  OptionSet : TSearchOptionSet ;
begin
  OptionSet := [] ;
  if FindDlg.RadioButton2.Checked then
    OptionSet := OptionSet + [soBackward] ;
  if FindDlg.Checkbox1.Checked then
    OptionSet := OptionSet + [soMatchCase] ;
  if not Editor.Search (FindDlg.Edit1.Text, OptionSet) then
    MessageDlg ('Search String ''' + FindDlg.Edit1.Text + ''' not found', MtInformation, [MbOk], 0) ;
  if Editor.CanFocus then
    Editor.SetFocus ;
end ;

procedure TPovEdit.WordStarCommands1Click (Sender : TObject) ;
begin
  with WordStarCommands1 do
  begin
    Checked := not (Checked) ;
    DefaultController.EntryCommands.Table [1].IsActive := Checked ;
  end ;
end ;

procedure TPovEdit.EditorShowStatus (Sender : TObject ; LineNum : Longint ; ColNum : Integer) ;
begin
  StatusForm.Panel2.Caption := IntToStr (LineNum) + ' :' + IntToStr (ColNum) ;

  if Editor.Modified then
    StatusForm.Panel3.Caption := 'Modified'
  else
    StatusForm.Panel3.Caption := '' ;

  StatusForm.Panel4.Caption := 'Total : ' + IntToStr (Editor.LineCount) ;
  StatusForm.Panel5.Caption := 'Top : ' + IntToStr (Editor.TopLine) ;
  StatusForm.Panel6.Caption := 'Bytes : ' + IntToStr (Editor.TextLength) ;

  if Editor.InsertMode then
    StatusForm.Panel7.Caption := 'Insert'
  else
    StatusForm.Panel7.Caption := 'Overwrite' ;

  Edit1Click (Self) ;
end ;

procedure TPovEdit.Edit1Click (Sender : TObject) ;
var
  StartLine, StopLine : LongInt ;
  StartCol, StopCol   : Integer ;
  IsSelected          : Boolean ;
begin
  SelectAll1.Enabled := Editor.TextLength > 0 ;
  IsSelected := Editor.GetSelection (StartLine, StartCol, StopLine, StopCol) ;
  Undo1.Enabled := Editor.CanUndo ;
  Undo2.Enabled := Editor.CanUndo ;
  Redo1.Enabled := Editor.CanRedo ;
  Redo2.Enabled := Editor.CanRedo ;
  Copy1.Enabled := IsSelected ;
  Cut1.Enabled := IsSelected ;
  OpenClipboard (Handle) ;
  Paste1.Enabled := GetClipboardData (Cf_Text) <> 0 ;
  CloseClipboard ;
end ;

procedure TPovEdit.AutoIndentation1Click (Sender : TObject) ;
var
  I : Integer ;
begin
  AutoIndentation1.Checked := not (AutoIndentation1.Checked) ;
  for I := 1 to EditorCount do
    Editors [I].AutoIndent := AutoIndentation1.Checked ;
end ;

procedure TPovEdit.CreateBackupFile1Click (Sender : TObject) ;
var
  I : Integer ;
begin
  CreateBackupFile1.Checked := not (CreateBackupFile1.Checked) ;
  for I := 1 to EditorCount do
    Editors [I].MakeBackup := CreateBackupFile1.Checked ;
end ;

procedure TPovEdit.TabTypeClick (Sender : TObject) ;
var
  TabStr : String ;
  I : Integer ;
begin
  FixedTabs1.Checked := False ;
  RealTabs1.Checked := False ;
  SmartTabs1.Checked := False ;
  TMenuItem (Sender).Checked := True ;

  if FixedTabs1.Checked then
    for I := 1 to EditorCount do
      Editors [I].TabType := ttFixed ;
  if RealTabs1.Checked then
    for I := 1 to EditorCount do
      Editors [I].TabType := ttReal ;
  if SmartTabs1.Checked then
    for I := 1 to EditorCount do
      Editors [I].TabType := ttSmart ;
end ;

procedure TPovEdit.NewFileClick (Sender : TObject) ;
var
  OkToClose : Boolean ;
begin
  SendMessage (MainWindow, CreateEditorMessage, 0, 0) ;
  Editor.NewFile ('Untitled') ;
  UpdateTab ;
  UpdateFileMenu ;
end ;

procedure TPovEdit.SaveAsClick (Sender : TObject) ;
var
  Path : String ;
begin
  Path := IniFile.ReadString ('Editor', 'LastPath', '') ;
  if Path <> '' then
    SaveDialog1.InitialDir := Path ;
  SaveDialog1.Title := 'Save ' + ExtractFileName (Editor.FileName) ;
  if Uppercase (Editor.Filename) = 'UNTITLED' then
    SaveDialog1.Filename := ''
  else
    SaveDialog1.FileName := Editor.FileName ;
  if SaveDialog1.Execute then
  begin
//  if CheckInstances (SaveDialog1.FileName) then
//  begin
//    PovShowMessage ('Error : You can''t use that filename because it''s already open in another window.') ;
//  end
//  else
    begin
      Editor.SaveToFile (SaveDialog1.FileName) ;
      Editor.FileName := SaveDialog1.FileName ;
      HistoryList.Add (Editor.FileName) ;
      UpdateTab ;
      try
        IniFile.WriteString ('Editor', 'LastPath', ExtractFilePath (Editor.FileName)) ;
      except
        on E : Exception do
        begin
          PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
        end ;
      end ;
      UpdateHistory ;
    end ;
  end ;
end ;

procedure TPovEdit.InsertMenuClicked (Sender : TObject) ;
var
  TPos                 : Integer ;
  WhichTemplate        : Integer ;
  ThisTemplate         : TextList ;
  CurrentLine          : TextPtr ;
  DoneWithTemplates    : Boolean ;
  DoneWithThisTemplate : Boolean ;
  l1, l2               : LongInt ;
  c1, c2               : Integer ;
  line                 : LongInt ;
  col                  : Integer ;
begin
  with TMenuItem (Sender) do
  begin
    {-------------------------------------------------------------------------}
    { This procedure responds to any of the Insert menu items that get        }
    { clicked ... First we need to figure out which menu entry was clicked.   }
    { We could have just read the caption and found the template by comparing }
    { the caption to the template name, but I figured that might be dangerous.}
    { What if two entries in different menus had the same name?  I don't think}
    { there are, but I can imagine it happening.  So we're using a different  }
    { trick instead.  Each menu entry has a #0 (null) and an item number      }
    { appended at the end.  The null prevents the number from being displayed }
    { in the menu, but it's still there for  us to read out of the caption.   }
    {-------------------------------------------------------------------------}

    // is there a number appended ?
    TPos := Pos (#0, Caption) ;
    if TPos <> 0 then
    begin
     // yes, figure out what the number is
     WhichTemplate := StrToInt (Copy (Caption, TPos + 1, Length (Caption) - TPos)) ;
     // are there any templates stored ?
      If FirstTemplate <> nil then
      begin
        // yes, traverse the list to find one with the same number.
        ThisTemplate := FirstTemplate ;
        DoneWithTemplates := False ;
        while not (DoneWithTemplates) do
        begin
          If ThisTemplate^.ItemNumber = WhichTemplate then
          begin
            // Okay, we found it!
            With ThisTemplate^ do
            begin
              If FirstLine <> nil then
              begin
                // This piece makes sure that we don't overwrite existing text that might be selected.
                with Editor do
                begin
                  if GetSelection (L1, C1, L2, C2) then
                  begin
                    Line := GetCaretPosition (Col) ;
                    SetSelection (Line, Col, Line, Col, False) ;
                  end ;
                end ;
                CurrentLine := FirstLine ;
                DoneWithThisTemplate := False ;
                Editor.insert (#$0d) ;
                While not (DoneWithThisTemplate) do
                begin
                  Editor.Insert (CurrentLine^.TextLine) ;
                  if CurrentLine^.NextLine <> nil then
                  begin
                    Currentline := CurrentLine^.NextLine ;
                    // here we insert a line-break between the last line and the next.
                    Editor.insert (#$0d)
                  end
                  else
                    DoneWithThisTemplate := True ;
                end ;
                Editor.insert (#$0d)
              end
              else
                PovShowMessage ('Error : No text for this template.')
            end ;
            DoneWithTemplates := True
          end
          else
            if ThisTemplate^.ItemNumber <= WhichTemplate then
            begin
              If ThisTemplate^.NextItem = nil then
                DoneWithTemplates := True
              else
                ThisTemplate := ThisTemplate^.NextItem
            end
            else
              DoneWithTemplates := True
        end
      end
    end
  end
end ;

{-----------------------------------------------------------------------------}
{ This procedure traverses the list of template text, and spews it all out to }
{ a report ... This is just useful to make sure that the template file got    }
{ read in correctly.                                                          }
{-----------------------------------------------------------------------------}

procedure TPovEdit.ListOfTemplates ;
var
  ThisTemplate         : TextList ;
  DoneWithTemplates    : Boolean ;
  DoneWithThisTemplate : Boolean ;
  LogFile              : TextFile ;
  CurrentLine          : TextPtr ;
begin
  If FirstTemplate <> nil then
  begin
    AssignFile (LogFile, 'template.lst') ;
    Rewrite (LogFile) ;
    WriteLn (LogFile, 'List of Templates') ;
    WriteLn (LogFile, '-----------------') ;
    Flush (LogFile) ;

    ThisTemplate := FirstTemplate ;
    DoneWithTemplates := False ;
    While not (DoneWithTemplates) do
    begin
      With ThisTemplate^ do
      begin
        WriteLn (LogFile, 'Template #', ItemNumber) ;
        Flush (LogFile) ;
        CurrentLine := FirstLine ;
        DoneWithThisTemplate := False ;
        While not DoneWithThisTemplate do
        begin
          Writeln (LogFile, CurrentLine^.TextLine) ;
          Flush (LogFile) ;
          if Currentline^.NextLine = nil then
            DoneWithThisTemplate := True
          else
            Currentline := CurrentLine^.NextLine
        end
      end ;
      If ThisTemplate^.NextItem = nil then
        DoneWithTemplates := True
      else
        ThisTemplate := ThisTemplate^.NextItem
    end ;
    CloseFile (LogFile)
  end
  else
    PovShowMessage ('Template list is nil, can''t list it.') ;
end ;

function TPovEdit.GetEditor (Index : Integer) : TOvcTextFileEditor ;
begin
  case Index of
    0  : Result := Editor ;
    1  : Result := Editor1 ;
    2  : Result := Editor2 ;
    3  : Result := Editor3 ;
    4  : Result := Editor4 ;
    5  : Result := Editor5 ;
    6  : Result := Editor6 ;
    7  : Result := Editor7 ;
    8  : Result := Editor8 ;
    9  : Result := Editor9 ;
    10 : Result := Editor10 ;
    11 : Result := Editor11 ;
    12 : Result := Editor12 ;
    13 : Result := Editor13 ;
    14 : Result := Editor14 ;
    15 : Result := Editor15 ;
    16 : Result := Editor16 ;
    else Result := nil ;
  end ;
end ;

procedure TPovEdit.FormCreate (Sender : TObject) ;
var
  NewLine                      : String ;
  LineTag                      : String ;
  FName, Path                  : String ;
  TempStr                      : String ;
  TemplateFileName             : String ;
  FileCtr                      : Integer ;
  TempInt, ErrorCode           : Integer ;
  RecentNum, TCtr              : Integer ;
  I                            : Integer ;
  NewItemNumber                : LongInt ;
  TempBoolean                  : Boolean ;
  TemplateFile                 : TextFile ;
  Lev1MenuItem                 : TMenuItem ;
  Lev2MenuItem                 : TMenuItem ;
  Lev3MenuItem                 : TMenuItem ;
  Lev4MenuItem                 : TMenuItem ;
  Search                       : TSearchRec ;
begin
  EditorActive := False ;
  Editor := Editor1 ;
  for I := 1 to EditorCount do
    Editors [I].FileName := '' ;

  try
    IniFile := TIniFile.Create (Home + 'ini\pvengine.ini') ;
  except
    PovShowMessage ('Cannot open INI file ''' + Home + 'ini\pvengine.ini''') ;
    Destroy ;
    exit ;
  end ;

  for I := 1 to EditorCount do
    Editors [I].Hide ;

  TempStr := IniFile.ReadString ('Editor', 'FontName', 'BorTE') ;
  for I := 1 to EditorCount do
    Editors [I].FixedFont.Name := TempStr ;

  TempInt := IniFile.ReadInteger ('Editor', 'FontSize', 8) ;
  for I := 1 to EditorCount do
    Editors [I].FixedFont.Size := TempInt ;

  TempStr := IniFile.ReadString ('Editor', 'FontStyle', 'Regular') ;
  if TempStr = 'Italic' then
    for I := 1 to EditorCount do
      Editors [I].FixedFont.Style := [fsItalic]
  else if TempStr = 'Bold Italic' then
    for I := 1 to EditorCount do
      Editors [I].FixedFont.Style := [fsBold, fsItalic]
  else if TempStr = 'Bold' then
    for I := 1 to EditorCount do
      Editors [I].FixedFont.Style := [fsBold] ;

  TempInt := IniFile.ReadInteger ('Editor', 'TabSize', 4) ;
  for I := 1 to EditorCount do
    Editors [I].TabSize := TempInt ;

  FixedTabs1.Checked := False ;
  RealTabs1.Checked := False ;
  SmartTabs1.Checked := False ;

  TempStr := IniFile.ReadString ('Editor', 'TabType', 'Smart') ;
  if TempStr = 'Fixed' then
  begin
    for I := 1 to EditorCount do
      Editors [I].TabType := ttFixed ;
    FixedTabs1.Checked := True ;
  end
  else if TempStr = 'Real' then
  begin
    for I := 1 to EditorCount do
      Editors [I].TabType := ttReal ;
    RealTabs1.Checked := True ;
  end
  else if TempStr = 'Smart' then
  begin
    for I := 1 to EditorCount do
      Editors [I].TabType := ttSmart ;
    SmartTabs1.Checked := True ;
  end ;

  TempInt := Inifile.ReadInteger ('Editor', 'CreateBackup', 1) ;
  CreateBackupFile1.Checked := Boolean (TempInt) ;
  TempInt := Inifile.ReadInteger ('Editor', 'AutoIndent', 1) ;
  AutoIndentation1.Checked := Boolean (TempInt) ;

  TempBoolean := IniFile.ReadInteger ('Editor', 'WordStarCommands', 1) <> 0 ;
  DefaultController.EntryCommands.Table [1].IsActive := TempBoolean ;
  WordStarCommands1.Checked := TempBoolean ;

  // read the templates from the file.
  AssignFile (TemplateFile, Home + 'bin\Pov3Tmpl.txt') ;
  try
    Reset (TemplateFile) ;

    // Initialize the list of templates.
    FirstTemplate := nil ;
    LastTemplate := nil ;

    // We use these variables to store pointers to the most recent menu items added.
    Lev1MenuItem := nil ;
    Lev2MenuItem := nil ;
    Lev3MenuItem := nil ;
    Lev4MenuItem := nil ;
    NewItemNumber := 0 ;

    while not EOF (TemplateFile) do
    begin
      ReadLn (TemplateFile, NewLine) ;
      LineTag := LowerCase (Copy (NewLine, 1, 4)) ;
      // if the line begins with @com. then it's a comment
      if LineTag = '@com' then
      begin
      end
      // if the line begins with @mt1. then it's a level - 1 menu entry.
      else if LineTag = '@mt1' then
      begin
        // first, we create the menu entry, appending a null and the number for this item to the caption.
        Lev1MenuItem := TMenuItem.Create (WindowMenu) ;
        Lev1MenuItem.Caption := Copy (NewLine, 6, Length (NewLine) - 5) + #0 + IntToStr (NewItemNumber) ;
        Inc (NewItemNumber) ;

        // Connect this menu entry to the template-handler.
        Lev1MenuItem.OnClick := InsertMenuClicked ;

        // Add the new item to the insert menu.
        InsertMenu.Add (Lev1MenuItem) ;
      end
      // @mt2 is a second level menu entry..
      else if LineTag = '@mt2' then
      begin
        if Lev1MenuItem <> nil then
        begin
          Lev2MenuItem := TMenuItem.Create (WindowMenu) ;
          Lev2MenuItem.Caption := Copy (NewLine, 6, Length (NewLine) - 5) + #0 + IntToStr (NewItemNumber) ;
          Inc (NewItemNumber) ;
          Lev2MenuItem.OnClick := InsertMenuClicked ;
          if Lev1MenuItem.Count mod 30 = 29 then
            Lev2MenuItem.Break := mbBarBreak ;

          // add the new item to the most recent level - 1 menu.
          Lev1MenuItem.Add (Lev2MenuItem) ;
        end ;
      end
      // @mt3 is a third level menu entry. Hopefully we'll never see any, but if
      // somebody with real bad taste puts one in anyway, we'll be able to use it..
      else if LineTag='@mt3' then
      begin
        if Lev2MenuItem <> nil then
        begin
          Lev3MenuItem := TMenuItem.Create (WindowMenu) ;
          Lev3MenuItem.Caption := copy (NewLine, 6, Length (NewLine) - 5) + #0 + IntToStr (NewItemNumber) ;
          Inc (NewItemNumber) ;
          Lev3MenuItem.OnClick := InsertMenuClicked ;

          // Add the item to the most recent level - 2 entry.
          Lev2MenuItem.Add (Lev3MenuItem) ;
        end ;
      end
      // @mt4 is a fourth level menu entry.  See the comment about @mt3.
      else if LineTag = '@mt4' then
      begin
        if Lev4MenuItem <> nil then
        begin
          Lev4MenuItem := TMenuItem.Create (WindowMenu) ;
          Lev4MenuItem.Caption := Copy (NewLine, 6, Length (NewLine) - 5) + #0 + IntToStr (NewItemNumber) ;
          Inc (NewItemNumber) ;
          Lev4MenuItem.OnClick := InsertMenuClicked ;

          // Add the item to the most recent level-1 entry.
          Lev3MenuItem.Add (Lev4MenuItem) ;
         end ;
       end
      // If a line begins with @@@@ then we're all done.
      else if LineTag = '@@@@' then
        break
      // otherwise the current line is a new line of text for the current template.
      else
      begin
        if FirstTemplate = nil then
        begin
          New (FirstTemplate) ;
          With FirstTemplate^ do
          begin
            ItemNumber := NewItemNumber - 1 ;
            NextItem := nil ;
            New (FirstLine) ;
            With FirstLine^ do
            begin
              TextLine := StrAlloc (Length (NewLine) + 1) ;
              StrPCopy (TextLine, NewLine) ;
              NextLine := nil ;
            end ;
            LastLine := FirstLine ;
          end ;
          LastTemplate := FirstTemplate ;
        end
        else if LastTemplate^.ItemNumber <> (NewItemNumber - 1) then
        begin
          New (LastTemplate^.NextItem) ;
          LastTemplate := LastTemplate^.NextItem ;
          with LastTemplate^ do
          begin
            ItemNumber := NewItemNumber - 1 ;
            NextItem := nil ;
            New (FirstLine) ;
            With FirstLine^ do
            begin
              TextLine := StrAlloc (Length (NewLine) + 1) ;
              StrPCopy (TextLine, NewLine) ;
              NextLine := nil ;
            end ;
            LastLine := FirstLine ;
          end ;
        end
        else
        begin
          with LastTemplate^ do
          begin
            New (LastLine^.NextLine) ;
            LastLine := LastLine^.NextLine ;
            with LastLine^ do
            begin
              TextLine := StrAlloc (Length (NewLine) + 1) ;
              StrPCopy (TextLine, NewLine) ;
              NextLine := nil ;
              NextLine := nil ;
            end
          end
        end
      end
    end ;
    CloseFile (TemplateFile) ;
  except
    // cannot open template file, or other error
    on E: Exception do PovShowMessage ('Error processing template file (' + E.Message + ')') ;
  end ;

  // Initialize history list.
  HistoryList := TStringList.Create ;

  // Read any history items from ini file, insert in history list.
  HistoryList.Clear ;
  for FileCtr := 1 to 9 do
  begin
    FName := Inifile.ReadString ('Editor', 'Recent' + IntToStr (FileCtr), '') ;
    if FName <> '' then
      HistoryList.Add (FName) ;
  end ;
  UpdateFileMenu ;

  FName := '' ;
  Visible := True ;
  OnPopup (Self) ;
end ;

procedure TPovEdit.SaveClick (Sender : TObject) ;
begin
  if UpperCase (Editor.FileName) <> 'UNTITLED' then
  begin
    Editor.SaveToFile (Editor.FileName) ;
    try
      IniFile.WriteString ('Editor', 'LastPath', ExtractFilePath (Editor.FileName)) ;
    except
      on E : Exception do
      begin
        PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
      end ;
    end ;
  end
  else
    SaveAsClick (Self) ;
end ;

procedure TPovEdit.UndoClick (Sender : TObject) ;
begin
  Editor.Undo ;
end ;

procedure TPovEdit.RedoClick (Sender : TObject) ;
begin
  Editor.Redo ;
end ;

procedure TPovEdit.CutClick (Sender : TObject) ;
begin
  Editor.CutToClipboard ;
end ;

procedure TPovEdit.PasteClick (Sender : TObject) ;
begin
  Editor.PasteFromClipboard ;
end ;

procedure TPovEdit.Delete1Click (Sender : TObject) ;
begin
  Editor.ProcessCommand (ccDel, 0) ;
end ;

procedure TPovEdit.FormCloseQuery (Sender : TObject ; var CanClose : Boolean) ;
var
  ButtonSelected : Word ;
begin
  if Editor.Modified then
  begin
    ButtonSelected := MessageDlg ('Data has changed, do you want to save ?', mtConfirmation, mbYesNoCancel, 0) ;
    if ButtonSelected in [mrYes] then
    begin
      SaveClick (Self) ;
      CanClose := not Editor.Modified ;
    end
    else
      CanClose := ButtonSelected in [mrNo] ;
 end
 else
   CanClose := True ;
end ;

procedure TPovEdit.FormClose (Sender : TObject ; var Action : TCloseAction) ;
var
  DoneWithTemplates    : Boolean ;
  DoneWithThisTemplate : Boolean ;
  CurrentLine          : TextPtr ;
  NextLine             : TextPtr ;
  CurrentTemplate      : TextList ;
  NextTemplate         : TextList ;
begin
  if FirstTemplate <> nil then
  begin
    CurrentTemplate := FirstTemplate ;
    DoneWithTemplates := False ;
    while not DoneWithTemplates do
    begin
      With CurrentTemplate^ do
      begin
        CurrentLine := FirstLine ;
        DoneWithThisTemplate := False ;
        while not DoneWithThisTemplate do
        begin
          NextLine := CurrentLine^.NextLine ;
          StrDispose (CurrentLine^.TextLine) ;
          Dispose (CurrentLine) ;
          if NextLine = nil then
            DoneWithThisTemplate := True
          else
            CurrentLine := NextLine ;
        end ;
      end ;
      NextTemplate := CurrentTemplate^.NextItem ;
      Dispose (CurrentTemplate) ;
      if NextTemplate = nil then
        DoneWithTemplates := True
      else
        CurrentTemplate := NextTemplate ;
    end ;
  end ;
  Inifile.Free ;
end ;

function WhiteSpace (Ch : Char) : Boolean ;
begin
  Result := Ch in [' ', '.', ',', ';', '.', '<', '>', '[', ']', '-', '+', '"', '''', ':', '(', ')', '/', '*'] ;
end ;

procedure TPovEdit.POVRayContextHelpClick (Sender : TObject) ;
var
  HelpFile   : String ;
  TStr       : String ;
  Row, Col   : Integer ;
  LineLength : Integer ;
  TLine      : PAnsiChar ;
begin
  Row := Editor.GetCaretPosition (Col) ;
  LineLength := Editor.LineLength [Row] ;
  TLine := StrAlloc (LineLength + 1) ;
  Editor.GetLine (Row, TLine, LineLength) ;
  HelpFile := Home + 'help\povray30.hlp' ;

  if Col > LineLength then
    Col := LineLength - 1 ;

  if Col < 0 then
    exit ;

  while (Col > 0) and (WhiteSpace (TLine [Col])) do
    Dec (Col) ;

  while WhiteSpace (TLine [Col]) do
  begin
    Inc (Col) ;
    if Col > LineLength then
      exit ;
  end ;
  while (Col > 0) and not (WhiteSpace (TLine [Col - 1])) do
    Dec (Col) ;
  TStr := '' ;
  while (Col <= LineLength) and not (WhiteSpace (TLine [Col])) do
  begin
    TStr := TStr + TLine [Col] ;
    Inc (Col) ;
  end ;
  StrDispose (TLine) ;
  TLine := StrAlloc (Length (TStr) + 1) ;
  StrPCopy (TLine, TStr) ;
  WinHelp (Handle, PChar (HelpFile), Help_PartialKey, LongInt (TLine)) ;
  StrDispose (TLine) ;
end ;

procedure TPovEdit.Replace1Click (Sender : TObject) ;
var
  OptionSet : TSearchOptionSet ;
  Count     : LongInt ;
  Code      : Word ;
  Aborted   : Boolean ;
begin
  Aborted := False ;
  Editor.HideSelection := False ;
  ReplDlg.ActiveControl := ReplDlg.Edit1 ;
  ReplDlg.ShowModal ;
  if (ReplDlg.ModalResult = mrOK) or (ReplDlg.ModalResult = mrYes) then
  begin
    OptionSet := [] ;

    if ReplDlg.ModalResult = mrYes then
      OptionSet := OptionSet + [soReplaceAll]
    else
      OptionSet := OptionSet + [soReplace] ;

    if ReplDlg.BackwardRadio.Checked then
      OptionSet := OptionSet + [soBackward] ;

    if ReplDlg.MatchCaseCheck.Checked then
      OptionSet := OptionSet + [soMatchCase] ;

    if ReplDlg.WholeWordCheck.Checked then
      OptionSet := OptionSet + [soWholeWord] ;

    if ReplDlg.GlobalCheck.Checked then
      OptionSet := OptionSet + [soGlobal] ;

    Count := Editor.Replace (ReplDlg.Edit1.Text, ReplDlg.Edit2.Text, OptionSet) ;
    repeat
      Code := MessageDlg ('Replace this occurrence?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) ;

      if Code = mrYes then
        Count := Editor.Replace (ReplDlg.Edit1.Text, ReplDlg.Edit2.Text, OptionSet) ;

      if Code = mrNo then
      begin
        Editor.Deselect (not (soBackward in OptionSet)) ;
        Count := Editor.Replace (ReplDlg.Edit1.Text, ReplDlg.Edit2.Text, OptionSet) ;
      end ;

      if Code = mrCancel then
      begin
        Aborted := True ;
        Editor.Deselect (not (soBackward in OptionSet)) ;
        Count := -1 ;
      end ;
    until Count = -1 ;
    if (Count = -1) and not Aborted then
      MessageDlg ('Search String ''' + ReplDlg.Edit1.Text + ''' not found', mtInformation, [mbOk], 0) ;
    Search1Click (Self) ;
  end ;
  if Editor.CanFocus then
    Editor.SetFocus ;
  Editor.HideSelection := True ;
end ;

procedure TPovEdit.CloseWindowClick (Sender : TObject) ;
begin
  SendMessage (MainWindow, CloseEditorMessage, 0, LParam (Editor)) ;
  if Editor.CanFocus then
    Editor.SetFocus ;
end ;

procedure TPovEdit.Exit1Click (Sender : TObject) ;
begin
  SendMessage (MainWindow, WM_COMMAND, CM_FILEEXIT, 0) ;
end ;

procedure TPovEdit.NewWindowClick (Sender : TObject) ;
begin
  SendMessage (MainWindow, CreateEditorMessage, 0, 0) ;
end ;

procedure TPovEdit.OpenInNewClick (Sender : TObject) ;
begin
  SendMessage (MainWindow, CreateEditorMessage, 0, 0) ;
  OpenInCurrentClick (Sender) ;
end ;

procedure TPovEdit.CloseAllClick (Sender : TObject) ;
var
  I : Integer ;
begin
  for I := 1 to EditorCount do
    SendMessage (MainWindow, CloseEditorMessage, 0, LParam (Editors [I])) ;
  UpdateHistory ;
end ;

procedure TPovEdit.CopyPaneClick (Sender : TObject) ;
begin
  SendMessage (MainWindow, WM_COMMAND, CM_DUMPPANE, 0) ;
end ;

procedure TPovEdit.ClearMessagesClick (Sender : TObject) ;
begin
  SendMessage (MainWindow, WM_COMMAND, CM_CLEARMESSAGES, 0) ;
end ;

procedure TPovEdit.Render1Click (Sender : TObject) ;
begin
  SendMessage (MainWindow, EditorRenderMessage, Integer (Editor.Modified), Integer (PChar (Editor.FileName))) ;
end ;

function TPovEdit.FileIsLoaded (SourceEditor : TOvcTextFileEditor ; FileName : String) : TOvcTextFileEditor ;
var
  I : Integer ;
begin
  FileName := Uppercase (FileName) ;
  for I := 1 to EditorCount do
  begin
    if (Editors [I] <> SourceEditor) and (Uppercase (Editors [I].FileName) = FileName) then
    begin
      Result := Editors [I] ;
      exit ;
    end ;
  end ;
  Result := nil ;
end ;

procedure TPovEdit.CreateParams (var Params : TCreateParams) ;
begin
  inherited CreateParams (Params) ;
  Params.WndParent := ParentWindow ;
  Params.Style := WS_CHILD ;
end ;

function TPovEdit.GetNextEditor : TOvcTextFileEditor ;
var
  I : Integer ;
begin
  for I := 1 to EditorCount do
  begin
    if Editors [I].FileName = '' then
    begin
      Result := Editors [I] ;
      Result.FileName := 'Untitled' ;
      exit ;
    end ;
  end ;
  Result := nil ;
end ;

function TPovEdit.ReleaseEditor (TheEditor : TOvcTextFileEditor) : boolean ;
var
  OkToClose : Boolean ;
  LastEditor : TOvcTextFileEditor ;
begin
  Result := False ;
  LastEditor := Editor ;
  Editor := TheEditor ;
  FormCloseQuery (Self, OkToClose) ;
  if not OkToClose then
  begin
    Editor := LastEditor ;
    exit ;
  end ;
  Editor.NewFile ('Untitled') ;
  Editor.FileName := '' ;
  if LastEditor = TheEditor then
    UpdateFileMenu ;
  Editor := LastEditor ;
  Result := True ;
end ;

function TPovEdit.FindTab (Editor : TOvcTextFileEditor) : Integer ;
var
  Item : TTCItem ;
  I : Integer ;
begin
  for I := 1 to SendMessage (TabbedWindow, TCM_GETITEMCOUNT, 0, 0) - 1 do
  begin
    Item.Mask := TCIF_PARAM ;
    SendMessage (TabbedWindow, TCM_GETITEM, I, Longint (@Item)) ;
    if TOvcTextFileEditor (Item.lParam) = Editor then
    begin
      Result := I ;
      exit ;
    end ;
  end ;
  Result := 0 ;
end ;

procedure TPovEdit.UpdateTab ;
var
  Item : TTCItem ;
  Index : Integer ;
begin
  Index := FindTab (Editor) ;
  if Index <> 0 then
  begin
    Item.Mask := TCIF_TEXT ;
    Item.pszText := PChar (ExtractFileName (Editor.FileName)) ;
    SendMessage (TabbedWindow, TCM_SETITEM, Index, Longint (@Item)) ;
  end ;
end ;

function TPovEdit.ProgramCloseQuery : Boolean ;
var
  ButtonSelected : Word ;
  I : Integer ;
  OldEditor : TOvcTextFileEditor ;
begin
  Result := False ;
  OldEditor := Editor ;
  for I := 1 to EditorCount do
  begin
    Editor := Editors [I] ;
    if Editor.Modified then
    begin
      ButtonSelected := Application.MessageBox (PChar ('Data has changed, do you want to save ?'), PChar (Editor.FileName), MB_ICONEXCLAMATION or MB_YESNOCANCEL) ;
      case ButtonSelected of
        IDCANCEL : exit ;
        IDYES    : begin
                     SaveClick (Self) ;
                     if Editor.Modified then
                       exit ;
                   end ;
        IDNO     : ;
      end
    end
  end ;
  Result := True ;
end ;

procedure TPovEdit.SetMenuActive (Active : Boolean) ;
begin
  EditorActive := Active ;

  CopyPane.Visible := not Active ;
  Redo2.Visible := Active ;
  Undo2.Visible := Active ;
  SelectAll1.Visible := Active ;
  Delete1.Visible := Active ;
  Copy1.Visible := Active ;
  Paste1.Visible := Active ;
  Cut1.Visible := Active ;
  N6.Visible := Active ;
  N8.Visible := Active ;

  Copy1.Enabled := Active ;
  Redo1.Enabled := Active ;
  Redo2.Enabled := Active ;
  Undo1.Enabled := Active ;
  Undo2.Enabled := Active ;
  Cut1.Enabled := Active ;
  Paste1.Enabled := Active ;
  Delete1.Enabled := Active ;
  SelectAll1.Enabled := Active ;

  CloseWindow.Enabled := Active ;
  Print1.Enabled := Active ;
  PrintSetup1.Enabled := Active ;
  Save.Enabled := Active ;
  SaveAs.Enabled := Active ;
  SaveAll.Enabled := Active ;
  CloseAll.Enabled := Active ;
  OpenInCurrent.Enabled := Active ;
end ;

procedure TPovEdit.OnUserCommand (Sender : TObject ; Command : Word) ;
begin
  case Command of
    ccUser0 : POVRayContextHelpClick (Sender) ;
    ccUser1 : Find1Click (Sender) ;
    ccUser2 : Replace1Click (Sender) ;
    ccUser3 : FindNext1Click (Sender) ;
    ccUser4 : CloseWindowClick (Sender) ;
    ccUser5 : SaveClick (Sender) ;
    ccUser6 : GotoLine1Click (Sender) ;
  end ;
end ;

procedure TPovEdit.RenderOptions1Click (Sender : TObject) ;
begin
  PovShowMessage ('Not yet implemented') ;
end ;

procedure TPovEdit.WriteSettings ;
var
  TabStr : String ;
begin
  try
    IniFile.WriteInteger ('Editor', 'CreateBackup', Integer (CreateBackupFile1.Checked)) ;
    IniFile.WriteInteger ('Editor', 'AutoIndent', Integer (AutoIndentation1.Checked)) ;
    IniFile.WriteString ('Editor', 'FontName', Editor.FixedFont.Name) ;
    IniFile.WriteInteger ('Editor', 'FontSize', Editor.FixedFont.Size) ;

    if (fsBold in Editor.FixedFont.Style) and (fsItalic in Editor.FixedFont.Style) then
      IniFile.WriteString ('Editor', 'FontStyle', 'Bold Italic')
    else if fsBold in Editor.FixedFont.Style then
      IniFile.WriteString ('Editor', 'FontStyle', 'Bold')
    else if fsItalic in Editor.FixedFont.Style then
      IniFile.WriteString ('Editor', 'FontStyle', 'Italic') ;

    IniFile.WriteInteger ('Editor', 'TabSize', Editor.TabSize) ;

    case Editor.TabType of
      ttFixed : TabStr := 'Fixed' ;
      ttReal  : TabStr := 'Real' ;
      ttSmart : TabStr := 'Smart' ;
    end ;
    IniFile.WriteString ('Editor', 'TabType', TabStr) ;
    IniFile.WriteInteger ('Editor', 'WordStarCommands', Integer (DefaultController.EntryCommands.Table [1].IsActive)) ;
  except
    on E : Exception do
    begin
      PovShowMessage ('Cannot write editor settings to INI file (it may be read-only)') ;
    end ;
  end ;
end ;

procedure TPovEdit.FormResize (Sender : TObject) ;
begin
  StatusForm.Left := Left ;
  StatusForm.Width := Width ;
  StatusForm.Height := StatusForm.Panel1.Height ;
  DividerPanel.Visible := Boolean (SendMessage (MainWindow, ShowMessagesMessage, 1, 0)) ;
end ;

procedure TPovEdit.WndProc (var Message : TMessage) ;
begin
  if StatusForm <> nil then
  begin
    if Message.Msg = WM_SHOWWINDOW then
    begin
      if Boolean (Message.WParam) then
        StatusForm.Show
      else
        StatusForm.Hide ;
    end ;
  end ;
  inherited WndProc (Message) ;
end ;

procedure TPovEdit.ToggleMessageDisplay1Click (Sender : TObject) ;
begin
  if SendMessage (MainWindow, ShowMessagesMessage, 0, 0) = 0 then
    ToggleMessageDisplay1.Caption := 'Show &Messages'
  else
    ToggleMessageDisplay1.Caption := 'Hide &Messages' ;
end ;

procedure TPovEdit.SaveAllClick (Sender : TObject) ;
var
  I : Integer ;
  OldEditor : TOvcTextFileEditor ;
begin
  OldEditor := Editor ;
  for I := 1 to EditorCount do
  begin
    if Editor.Filename <> '' then
    begin
      Editor := Editors [I] ;
      SaveClick (Self) ;
    end ;
  end ;
  Editor := OldEditor ;
end ;

procedure TPovEdit.EditorError (Sender : TObject ; var ErrorCode : Word) ;
begin
  case ErrorCode of
    oeOutOfMemory  : PovShowMessage ('Editor : There is not sufficient memory to perform the requested operation.') ;
    oeRegionSize   : PovShowMessage ('Editor : The selected text exceeds 64KB and cannot be copied to the clipboard.') ;
    oeTooManyParas : PovShowMessage ('Editor : The limit on the number of lines was exceeded.') ;
    oeCannotJoin   : PovShowMessage ('Editor : The lines cannot be joined.') ;
    oeTooManyBytes : PovShowMessage ('Editor : The limit on the total number of bytes was exceeded.') ;
    oeParaTooLong  : PovShowMessage ('Editor : The limit on the length of an individual line was exceeded.') ;
    else             PovShowMessage ('Editor : Unknown error code.') ;
  end ;
end ;

end.


