Subversion Repositories fastphp

Rev

Rev 104 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 104 Rev 105
1
unit EditorMain;
1
unit EditorMain;
2
 
2
 
3
{$Include 'FastPHP.inc'}
3
{$Include 'FastPHP.inc'}
4
 
4
 
5
(*
5
(*
6
  This program requires
6
  This program requires
7
  - Microsoft Internet Controls (TWebBrowser)
7
  - Microsoft Internet Controls (TWebBrowser)
8
    If you are using Delphi 10.1 Starter Edition, please import the ActiveX TLB
8
    If you are using Delphi 10.1 Starter Edition, please import the ActiveX TLB
9
    "Microsoft Internet Controls"
9
    "Microsoft Internet Controls"
10
  - SynEdit
10
  - SynEdit
11
    You can obtain SynEdit via Embarcadero GetIt
11
    You can obtain SynEdit via Embarcadero GetIt
12
*)
12
*)
13
 
13
 
14
// TODO: "crHourGlass" does not work if F9 is pressed!
14
// TODO: "crHourGlass" does not work if F9 is pressed!
15
// TODO: if a scrapfile is already open, create a new scrap file (scrap2.php)
15
// TODO: if a scrapfile is already open, create a new scrap file (scrap2.php)
16
// TODO: localize
16
// TODO: localize
17
// TODO: wieso geht copy paste im twebbrowser nicht???
17
// TODO: wieso geht copy paste im twebbrowser nicht???
18
// TODO: Wieso dauert webbrowser1 erste kompilierung so lange???
18
// TODO: Wieso dauert webbrowser1 erste kompilierung so lange???
19
// TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
19
// TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
20
// TODO: Browser titlebar (link preview)
20
// TODO: Browser titlebar (link preview)
21
// TODO: "jump to next/prev todo" buttons/shortcuts
21
// TODO: "jump to next/prev todo" buttons/shortcuts
22
// TODO: "increase/decrease indent" buttons/shortcuts
22
// TODO: "increase/decrease indent" buttons/shortcuts
23
// TODO: Solve all compiler warnings, especially in regards Encoding
23
// TODO: Solve all compiler warnings, especially in regards Encoding
24
 
24
 
25
// Note in re Unicode:
25
// Note in re Unicode:
26
// - In Embarcadero® Delphi 10.4 Version 27.0.40680.4203:
26
// - In Embarcadero® Delphi 10.4 Version 27.0.40680.4203:
27
//   SynEdit correctly detects UTF-8 files without BOM as well as ANSI files with Umlauts.
27
//   SynEdit correctly detects UTF-8 files without BOM as well as ANSI files with Umlauts.
28
//   (Previous versions could not detect UTF-8 files without BOM?!)
28
//   (Previous versions could not detect UTF-8 files without BOM?!)
29
// - If BOM is existing, it will be removed. (which is good, because this is defined by PSR-1)
29
// - If BOM is existing, it will be removed. (which is good, because this is defined by PSR-1)
30
 
30
 
31
// Small things:
31
// Small things:
32
// - The scroll bars of SynEdit are not affected by the dark theme
32
// - The scroll bars of SynEdit are not affected by the dark theme
33
// - dark theme full screen: doubleclick to desktop pixel "0,0" cannot be used to close the app
33
// - dark theme full screen: doubleclick to desktop pixel "0,0" cannot be used to close the app
34
 
34
 
35
// Future ideas
35
// Future ideas
36
// - code insight
36
// - code insight
37
// - verschiedene php versionen?
37
// - verschiedene php versionen?
38
// - webbrowser1 nur laden, wenn man den tab anwaehlt?
38
// - webbrowser1 nur laden, wenn man den tab anwaehlt?
39
// - doppelklick auf tab soll diesen schliessen
39
// - doppelklick auf tab soll diesen schliessen
40
// - Onlinehelp (www) aufrufen oder CHM datei
40
// - Onlinehelp (www) aufrufen oder CHM datei
41
// - Let all colors be adjustable
41
// - Let all colors be adjustable
42
// - code in bildschirmmitte (horizontal)?
42
// - code in bildschirmmitte (horizontal)?
43
// - search in files of a directory
43
// - search in files of a directory
44
// - Files in multiple tabs?
44
// - Files in multiple tabs?
45
// - Configurable tabulator display-width
45
// - Configurable tabulator display-width
46
 
46
 
47
interface
47
interface
48
 
48
 
49
uses
49
uses
50
  // TODO: "{$IFDEF USE_SHDOCVW_TLB}_TLB{$ENDIF}" does not work with Delphi 10.2
50
  // TODO: "{$IFDEF USE_SHDOCVW_TLB}_TLB{$ENDIF}" does not work with Delphi 10.2
51
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
51
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
52
  Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
52
  Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
53
  SynEditHighlighter, SynHighlighterPHP, SynEdit, ShDocVw, FindReplace,
53
  SynEditHighlighter, SynHighlighterPHP, SynEdit, ShDocVw, FindReplace,
54
  ActnList, SynEditMiscClasses, SynEditSearch, RunPHP, ImgList, SynUnicode,
54
  ActnList, SynEditMiscClasses, SynEditSearch, RunPHP, ImgList, SynUnicode,
55
  System.ImageList, System.Actions, Vcl.Menus, Vcl.Themes, System.UITypes,
55
  System.ImageList, System.Actions, Vcl.Menus, Vcl.Themes, System.UITypes,
56
  SynEditCodeFolding;
56
  SynEditCodeFolding, WebBrowserUtils;
57
 
57
 
58
{.$DEFINE OnlineHelp}
58
{.$DEFINE OnlineHelp}
59
 
59
 
60
type
60
type
61
  TForm1 = class(TForm)
61
  TForm1 = class(TForm)
62
    PageControl1: TPageControl;
62
    PageControl1: TPageControl;
63
    PlaintextTabSheet: TTabSheet;
63
    PlaintextTabSheet: TTabSheet;
64
    HtmlTabSheet: TTabSheet;
64
    HtmlTabSheet: TTabSheet;
65
    Memo2: TMemo;
65
    Memo2: TMemo;
66
    WebBrowser1: TWebBrowser;
66
    WebBrowser1: TWebBrowser;
67
    Splitter1: TSplitter;
67
    Splitter1: TSplitter;
68
    PageControl2: TPageControl;
68
    PageControl2: TPageControl;
69
    CodeTabsheet: TTabSheet;
69
    CodeTabsheet: TTabSheet;
70
    HelpTabsheet: TTabSheet;
70
    HelpTabsheet: TTabSheet;
71
    WebBrowser2: TWebBrowser;
71
    WebBrowser2: TWebBrowser;
72
    OpenDialog1: TOpenDialog;
72
    OpenDialog1: TOpenDialog;
73
    Panel1: TPanel;
73
    Panel1: TPanel;
74
    OpenDialog3: TOpenDialog;
74
    OpenDialog3: TOpenDialog;
75
    SynEdit1: TSynEdit;
75
    SynEdit1: TSynEdit;
76
    SynPHPSyn1: TSynPHPSyn;
76
    SynPHPSyn1: TSynPHPSyn;
77
    Panel2: TPanel;
77
    Panel2: TPanel;
78
    SynEditFocusTimer: TTimer;
78
    SynEditFocusTimer: TTimer;
79
    Button1: TButton;
79
    Button1: TButton;
80
    Button2: TButton;
80
    Button2: TButton;
81
    Button3: TButton;
81
    Button3: TButton;
82
    Button4: TButton;
82
    Button4: TButton;
83
    Button5: TButton;
83
    Button5: TButton;
84
    Button6: TButton;
84
    Button6: TButton;
85
    ActionList: TActionList;
85
    ActionList: TActionList;
86
    ActionFind: TAction;
86
    ActionFind: TAction;
87
    ActionReplace: TAction;
87
    ActionReplace: TAction;
88
    ActionFindNext: TAction;
88
    ActionFindNext: TAction;
89
    ActionGoto: TAction;
89
    ActionGoto: TAction;
90
    ActionSave: TAction;
90
    ActionSave: TAction;
91
    ActionHelp: TAction;
91
    ActionHelp: TAction;
92
    ActionRun: TAction;
92
    ActionRun: TAction;
93
    ActionESC: TAction;
93
    ActionESC: TAction;
94
    Button7: TButton;
94
    Button7: TButton;
95
    ActionOpen: TAction;
95
    ActionOpen: TAction;
96
    Button8: TButton;
96
    Button8: TButton;
97
    Button9: TButton;
97
    Button9: TButton;
98
    ActionFindPrev: TAction;
98
    ActionFindPrev: TAction;
99
    Timer1: TTimer;
99
    Timer1: TTimer;
100
    ActionSpaceToTab: TAction;
100
    ActionSpaceToTab: TAction;
101
    Button11: TButton;
101
    Button11: TButton;
102
    SynEditSearch1: TSynEditSearch;
102
    SynEditSearch1: TSynEditSearch;
103
    TreeView1: TTreeView;
103
    TreeView1: TTreeView;
104
    Splitter2: TSplitter;
104
    Splitter2: TSplitter;
105
    btnLint: TButton;
105
    btnLint: TButton;
106
    ActionLint: TAction;
106
    ActionLint: TAction;
107
    ImageList1: TImageList;
107
    ImageList1: TImageList;
108
    RunPopup: TPopupMenu;
108
    RunPopup: TPopupMenu;
109
    OpeninIDE1: TMenuItem;
109
    OpeninIDE1: TMenuItem;
110
    ActionRunConsole: TAction;
110
    ActionRunConsole: TAction;
111
    Runinconsole1: TMenuItem;
111
    Runinconsole1: TMenuItem;
112
    SavePopup: TPopupMenu;
112
    SavePopup: TPopupMenu;
113
    Saveas1: TMenuItem;
113
    Saveas1: TMenuItem;
114
    Save1: TMenuItem;
114
    Save1: TMenuItem;
115
    SaveDialog1: TSaveDialog;
115
    SaveDialog1: TSaveDialog;
116
    BtnSpecialChars: TImage;
116
    BtnSpecialChars: TImage;
117
    BtnSpecialCharsOff: TImage;
117
    BtnSpecialCharsOff: TImage;
118
    BtnSpecialCharsOn: TImage;
118
    BtnSpecialCharsOn: TImage;
119
    BtnLightOn: TImage;
119
    BtnLightOn: TImage;
120
    BtnLightOff: TImage;
120
    BtnLightOff: TImage;
121
    BtnLight: TImage;
121
    BtnLight: TImage;
122
    StartUpTimer: TTimer;
122
    StartUpTimer: TTimer;
123
    FileModTimer: TTimer;
123
    FileModTimer: TTimer;
124
    GotoPHPdir1: TMenuItem;
124
    GotoPHPdir1: TMenuItem;
125
    PHPShell1: TMenuItem;
125
    PHPShell1: TMenuItem;
126
    ActionSaveAs: TAction;
126
    ActionSaveAs: TAction;
127
    ActionGoToPHPDir: TAction;
127
    ActionGoToPHPDir: TAction;
128
    ActionPHPInteractiveShell: TAction;
128
    ActionPHPInteractiveShell: TAction;
129
    FontSizeTimer: TTimer;
129
    FontSizeTimer: TTimer;
130
    procedure Run(Sender: TObject);
130
    procedure Run(Sender: TObject);
131
    procedure RunConsole(Sender: TObject);
131
    procedure RunConsole(Sender: TObject);
132
    procedure FormShow(Sender: TObject);
132
    procedure FormShow(Sender: TObject);
133
    procedure FormCreate(Sender: TObject);
133
    procedure FormCreate(Sender: TObject);
134
    procedure FormDestroy(Sender: TObject);
134
    procedure FormDestroy(Sender: TObject);
135
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
135
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
136
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
136
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
137
    procedure Memo2DblClick(Sender: TObject);
137
    procedure Memo2DblClick(Sender: TObject);
138
    (*
138
    (*
139
    {$IFDEF USE_SHDOCVW_TLB}
139
    {$IFDEF USE_SHDOCVW_TLB}
140
    *)
140
    *)
141
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
141
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
142
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
142
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
143
      Headers: OleVariant; var Cancel: WordBool);
143
      Headers: OleVariant; var Cancel: WordBool);
144
    (*
144
    (*
145
    {$ELSE}
145
    {$ELSE}
146
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
146
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
147
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
147
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
148
      Headers: OleVariant; var Cancel: WordBool);
148
      Headers: OleVariant; var Cancel: WordBool);
149
    {$ENDIF}
149
    {$ENDIF}
150
    *)
150
    *)
151
    procedure BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
151
    procedure BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
152
    procedure SynEditFocusTimerTimer(Sender: TObject);
152
    procedure SynEditFocusTimerTimer(Sender: TObject);
153
    procedure ActionFindExecute(Sender: TObject);
153
    procedure ActionFindExecute(Sender: TObject);
154
    procedure ActionReplaceExecute(Sender: TObject);
154
    procedure ActionReplaceExecute(Sender: TObject);
155
    procedure ActionFindNextExecute(Sender: TObject);
155
    procedure ActionFindNextExecute(Sender: TObject);
156
    procedure ActionGotoExecute(Sender: TObject);
156
    procedure ActionGotoExecute(Sender: TObject);
157
    procedure ActionSaveExecute(Sender: TObject);
157
    procedure ActionSaveExecute(Sender: TObject);
158
    procedure ActionHelpExecute(Sender: TObject);
158
    procedure ActionHelpExecute(Sender: TObject);
159
    procedure ActionRunExecute(Sender: TObject);
159
    procedure ActionRunExecute(Sender: TObject);
160
    procedure ActionESCExecute(Sender: TObject);
160
    procedure ActionESCExecute(Sender: TObject);
161
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
161
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
162
      MousePos: TPoint; var Handled: Boolean);
162
      MousePos: TPoint; var Handled: Boolean);
163
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
163
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
164
      MousePos: TPoint; var Handled: Boolean);
164
      MousePos: TPoint; var Handled: Boolean);
165
    procedure ActionOpenExecute(Sender: TObject);
165
    procedure ActionOpenExecute(Sender: TObject);
166
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
166
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
167
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
167
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
168
    procedure ActionFindPrevExecute(Sender: TObject);
168
    procedure ActionFindPrevExecute(Sender: TObject);
169
    procedure SynEdit1MouseCursor(Sender: TObject;
169
    procedure SynEdit1MouseCursor(Sender: TObject;
170
      const aLineCharPos: TBufferCoord; var aCursor: TCursor);
170
      const aLineCharPos: TBufferCoord; var aCursor: TCursor);
171
    procedure Timer1Timer(Sender: TObject);
171
    procedure Timer1Timer(Sender: TObject);
172
    procedure ActionSpaceToTabExecute(Sender: TObject);
172
    procedure ActionSpaceToTabExecute(Sender: TObject);
173
    procedure TreeView1DblClick(Sender: TObject);
173
    procedure TreeView1DblClick(Sender: TObject);
174
    procedure SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X, Y,
174
    procedure SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X, Y,
175
      Line: Integer; Mark: TSynEditMark);
175
      Line: Integer; Mark: TSynEditMark);
176
    procedure SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas;
176
    procedure SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas;
177
      TransientType: TTransientType);
177
      TransientType: TTransientType);
178
    procedure ActionLintExecute(Sender: TObject);
178
    procedure ActionLintExecute(Sender: TObject);
179
    procedure ActionRunConsoleExecute(Sender: TObject);
179
    procedure ActionRunConsoleExecute(Sender: TObject);
180
    procedure BtnSpecialCharsClick(Sender: TObject);
180
    procedure BtnSpecialCharsClick(Sender: TObject);
181
    procedure WebBrowser1WindowClosing(ASender: TObject;
181
    procedure WebBrowser1WindowClosing(ASender: TObject;
182
      IsChildWindow: WordBool; var Cancel: WordBool);
182
      IsChildWindow: WordBool; var Cancel: WordBool);
183
    procedure BtnLightClick(Sender: TObject);
183
    procedure BtnLightClick(Sender: TObject);
184
    procedure StartUpTimerTimer(Sender: TObject);
184
    procedure StartUpTimerTimer(Sender: TObject);
185
    procedure FileModTimerTimer(Sender: TObject);
185
    procedure FileModTimerTimer(Sender: TObject);
186
    procedure SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
186
    procedure SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
187
      AFiles: TStrings);
187
      AFiles: TStrings);
188
    procedure ActionSaveAsExecute(Sender: TObject);
188
    procedure ActionSaveAsExecute(Sender: TObject);
189
    procedure ActionGoToPHPDirExecute(Sender: TObject);
189
    procedure ActionGoToPHPDirExecute(Sender: TObject);
190
    procedure ActionPHPInteractiveShellExecute(Sender: TObject);
190
    procedure ActionPHPInteractiveShellExecute(Sender: TObject);
191
    procedure SynEdit1KeyDown(Sender: TObject; var Key: Word;
191
    procedure SynEdit1KeyDown(Sender: TObject; var Key: Word;
192
      Shift: TShiftState);
192
      Shift: TShiftState);
193
    procedure FontSizeTimerTimer(Sender: TObject);
193
    procedure FontSizeTimerTimer(Sender: TObject);
194
    procedure SynEdit1StatusChange(Sender: TObject; Changes: TSynStatusChanges);
194
    procedure SynEdit1StatusChange(Sender: TObject; Changes: TSynStatusChanges);
195
  private
195
  private
196
    hMutex: THandle;
196
    hMutex: THandle;
197
    CurSearchTerm: string;
197
    CurSearchTerm: string;
198
    HlpPrevPageIndex: integer;
198
    HlpPrevPageIndex: integer;
199
    SrcRep: TSynEditFindReplace;
199
    SrcRep: TSynEditFindReplace;
200
    {$IFDEF OnlineHelp}
200
    {$IFDEF OnlineHelp}
201
    gOnlineHelpWord: string;
201
    gOnlineHelpWord: string;
202
    {$ENDIF}
202
    {$ENDIF}
203
    FileModLast: TDateTime;
203
    FileModLast: TDateTime;
204
    FormShowRanOnce: boolean;
204
    FormShowRanOnce: boolean;
205
    BrowserLoadedOnce: boolean;
205
    BrowserLoadedOnce: boolean;
206
    procedure Help;
206
    procedure Help;
207
    function InputRequestCallback(var data: AnsiString): boolean;
207
    function InputRequestCallback(var data: AnsiString): boolean;
208
    function OutputNotifyCallback(const data: AnsiString): boolean;
208
    function OutputNotifyCallback(const data: AnsiString): boolean;
209
    procedure RightTrimAll;
209
    procedure RightTrimAll;
210
  protected
210
  protected
211
    ChmIndex: TMemIniFile;
211
    ChmIndex: TMemIniFile;
212
    FScrapFile: string;
212
    FScrapFile: string;
213
    FSaveAsFilename: string;
213
    FSaveAsFilename: string;
214
    codeExplorer: TRunCodeExplorer;
214
    codeExplorer: TRunCodeExplorer;
215
    procedure GotoLineNo(LineNo: integer);
215
    procedure GotoLineNo(LineNo: integer);
216
    function GetScrapFile: string;
216
    function GetScrapFile: string;
217
    procedure StartCodeExplorer;
217
    procedure StartCodeExplorer;
218
    procedure RefreshModifySign;
218
    procedure RefreshModifySign;
219
    procedure Theme_Light;
219
    procedure Theme_Light;
220
    procedure Theme_Dark;
220
    procedure Theme_Dark;
221
    function IsThemeDark: boolean;
221
    function IsThemeDark: boolean;
222
    function MarkUpLineReference(cont: string): string;
222
    function MarkUpLineReference(cont: string): string;
223
    function ContainsUnicodeCharsInAnsiFile: boolean;
223
    function ContainsUnicodeCharsInAnsiFile: boolean;
224
    procedure SaveToFile(filename: string);
224
    procedure SaveToFile(filename: string);
225
  end;
225
  end;
226
 
226
 
227
var
227
var
228
  Form1: TForm1;
228
  Form1: TForm1;
229
 
229
 
230
implementation
230
implementation
231
 
231
 
232
{$R *.dfm}
232
{$R *.dfm}
233
 
233
 
234
{$R Cursors.res}
234
{$R Cursors.res}
235
 
235
 
236
uses
236
uses
237
  Functions, StrUtils, WebBrowserUtils, FastPHPUtils, Math, ShellAPI, RichEdit,
237
  Functions, StrUtils, FastPHPUtils, Math, ShellAPI, RichEdit,
238
  FastPHPTreeView, ImageListEx, FastPHPConfig;
238
  FastPHPTreeView, ImageListEx, FastPHPConfig;
239
 
239
 
240
const
240
const
241
  crMouseGutter = 1;
241
  crMouseGutter = 1;
242
 
242
 
243
procedure TForm1.RefreshModifySign;
243
procedure TForm1.RefreshModifySign;
244
var
244
var
245
  tmp: string;
245
  tmp: string;
246
begin
246
begin
247
  if SynEdit1 = nil then exit;
247
  if SynEdit1 = nil then exit;
248
 
248
 
249
  tmp := Caption;
249
  tmp := Caption;
250
 
250
 
251
  tmp := StringReplace(tmp, '*', '', [rfReplaceAll]);
251
  tmp := StringReplace(tmp, '*', '', [rfReplaceAll]);
252
  if SynEdit1.Modified then tmp := tmp + '*';
252
  if SynEdit1.Modified then tmp := tmp + '*';
253
 
253
 
254
  if Caption <> tmp then Caption := tmp;
254
  if Caption <> tmp then Caption := tmp;
255
end;
255
end;
256
 
256
 
257
procedure TForm1.ActionFindNextExecute(Sender: TObject);
257
procedure TForm1.ActionFindNextExecute(Sender: TObject);
258
begin
258
begin
259
  SrcRep.FindNext;
259
  SrcRep.FindNext;
260
end;
260
end;
261
 
261
 
262
procedure TForm1.ActionFindPrevExecute(Sender: TObject);
262
procedure TForm1.ActionFindPrevExecute(Sender: TObject);
263
begin
263
begin
264
  SrcRep.FindPrev;
264
  SrcRep.FindPrev;
265
end;
265
end;
266
 
266
 
267
procedure TForm1.ActionGotoExecute(Sender: TObject);
267
procedure TForm1.ActionGotoExecute(Sender: TObject);
268
var
268
var
269
  val: string;
269
  val: string;
270
  lineno: integer;
270
  lineno: integer;
271
resourcestring
271
resourcestring
272
  SGoTo = 'Go to';
272
  SGoTo = 'Go to';
273
  SLineNumber = 'Line number:';
273
  SLineNumber = 'Line number:';
274
begin
274
begin
275
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
275
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
276
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
276
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
277
 
277
 
278
  if not InputQuery(SGoTo, SLineNumber, val) or
278
  if not InputQuery(SGoTo, SLineNumber, val) or
279
     not TryStrToInt(val, lineno) then
279
     not TryStrToInt(val, lineno) then
280
  begin
280
  begin
281
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
281
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
282
    exit;
282
    exit;
283
  end;
283
  end;
284
  GotoLineNo(lineno);
284
  GotoLineNo(lineno);
285
end;
285
end;
286
 
286
 
287
procedure TForm1.ActionGoToPHPDirExecute(Sender: TObject);
287
procedure TForm1.ActionGoToPHPDirExecute(Sender: TObject);
288
var
288
var
289
  phpExe: string;
289
  phpExe: string;
290
begin
290
begin
291
  phpExe := GetPHPExe;
291
  phpExe := GetPHPExe;
292
  if phpExe <> '' then
292
  if phpExe <> '' then
293
    ShellExecute(Handle, 'open', 'explorer.exe', PChar(ExtractFilePath(phpExe)), '', SW_NORMAL);
293
    ShellExecute(Handle, 'open', 'explorer.exe', PChar(ExtractFilePath(phpExe)), '', SW_NORMAL);
294
end;
294
end;
295
 
295
 
296
procedure TForm1.ActionHelpExecute(Sender: TObject);
296
procedure TForm1.ActionHelpExecute(Sender: TObject);
297
begin
297
begin
298
  Help;
298
  Help;
299
  if PageControl2.ActivePage = HelpTabsheet then
299
  if PageControl2.ActivePage = HelpTabsheet then
300
    WebBrowser2.SetFocus
300
    WebBrowser2.SetFocus
301
  else if PageControl2.ActivePage = CodeTabsheet then
301
  else if PageControl2.ActivePage = CodeTabsheet then
302
    SynEdit1.SetFocus;
302
    SynEdit1.SetFocus;
303
end;
303
end;
304
 
304
 
305
procedure TForm1.ActionLintExecute(Sender: TObject);
305
procedure TForm1.ActionLintExecute(Sender: TObject);
306
begin
306
begin
307
  Run(Sender);
307
  Run(Sender);
308
  SynEdit1.SetFocus;
308
  SynEdit1.SetFocus;
309
end;
309
end;
310
 
310
 
311
procedure TForm1.ActionOpenExecute(Sender: TObject);
311
procedure TForm1.ActionOpenExecute(Sender: TObject);
312
begin
312
begin
313
  If OpenDialog3.Execute then
313
  If OpenDialog3.Execute then
314
  begin
314
  begin
315
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + OpenDialog3.FileName + '"'), '', SW_NORMAL);
315
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + OpenDialog3.FileName + '"'), '', SW_NORMAL);
316
  end;
316
  end;
317
end;
317
end;
318
 
318
 
319
procedure TForm1.ActionPHPInteractiveShellExecute(Sender: TObject);
319
procedure TForm1.ActionPHPInteractiveShellExecute(Sender: TObject);
320
var
320
var
321
  phpExe: string;
321
  phpExe: string;
322
begin
322
begin
323
  phpExe := GetPHPExe;
323
  phpExe := GetPHPExe;
324
  if phpExe <> '' then
324
  if phpExe <> '' then
325
    ShellExecute(Handle, 'open', PChar(phpExe), '-a', PChar(ExtractFilePath(phpExe)), SW_NORMAL);
325
    ShellExecute(Handle, 'open', PChar(phpExe), '-a', PChar(ExtractFilePath(phpExe)), SW_NORMAL);
326
end;
326
end;
327
 
327
 
328
procedure TForm1.ActionReplaceExecute(Sender: TObject);
328
procedure TForm1.ActionReplaceExecute(Sender: TObject);
329
begin
329
begin
330
  SrcRep.ReplaceExecute;
330
  SrcRep.ReplaceExecute;
331
end;
331
end;
332
 
332
 
333
procedure TForm1.ActionRunConsoleExecute(Sender: TObject);
333
procedure TForm1.ActionRunConsoleExecute(Sender: TObject);
334
begin
334
begin
335
  RunConsole(Sender);
335
  RunConsole(Sender);
336
  SynEdit1.SetFocus;
336
  SynEdit1.SetFocus;
337
end;
337
end;
338
 
338
 
339
procedure TForm1.ActionRunExecute(Sender: TObject);
339
procedure TForm1.ActionRunExecute(Sender: TObject);
340
begin
340
begin
341
  Run(Sender);
341
  Run(Sender);
342
  SynEdit1.SetFocus;
342
  SynEdit1.SetFocus;
343
end;
343
end;
344
 
344
 
345
procedure TForm1.RightTrimAll;
345
procedure TForm1.RightTrimAll;
346
var
346
var
347
  i: integer;
347
  i: integer;
348
begin
348
begin
349
  for i := 0 to SynEdit1.Lines.Count-1 do
349
  for i := 0 to SynEdit1.Lines.Count-1 do
350
  begin
350
  begin
351
    SynEdit1.Lines.Strings[i] := TrimRight(SynEdit1.Lines.Strings[i]);
351
    SynEdit1.Lines.Strings[i] := TrimRight(SynEdit1.Lines.Strings[i]);
352
  end;
352
  end;
353
 
353
 
354
  (*
354
  (*
355
  while (SynEdit1.Lines.Count > 0) and (SynEdit1.Lines.Strings[SynEdit1.Lines.Count-1] = '') do
355
  while (SynEdit1.Lines.Count > 0) and (SynEdit1.Lines.Strings[SynEdit1.Lines.Count-1] = '') do
356
  begin
356
  begin
357
    SynEdit1.Lines.Delete(SynEdit1.Lines.Count-1);
357
    SynEdit1.Lines.Delete(SynEdit1.Lines.Count-1);
358
  end;
358
  end;
359
  if SynEdit1.SelStart > Length(SynEdit1.Text)-1 then
359
  if SynEdit1.SelStart > Length(SynEdit1.Text)-1 then
360
  begin
360
  begin
361
    // TODO: This code does not work...
361
    // TODO: This code does not work...
362
    SynEdit1.SelStart := Length(SynEdit1.Text)-1;
362
    SynEdit1.SelStart := Length(SynEdit1.Text)-1;
363
    SynEdit1.SelEnd   := Length(SynEdit1.Text)-1;
363
    SynEdit1.SelEnd   := Length(SynEdit1.Text)-1;
364
  end;
364
  end;
365
  *)
365
  *)
366
end;
366
end;
367
 
367
 
368
procedure TForm1.ActionSaveAsExecute(Sender: TObject);
368
procedure TForm1.ActionSaveAsExecute(Sender: TObject);
369
var
369
var
370
  hMutexNew: THandle;
370
  hMutexNew: THandle;
371
resourcestring
371
resourcestring
372
  SCannotSaveBecauseMutex = 'Cannot save because file "%s", because it is alrady open in another FastPHP window!';
372
  SCannotSaveBecauseMutex = 'Cannot save because file "%s", because it is alrady open in another FastPHP window!';
373
begin
373
begin
374
  if SaveDialog1.Execute then
374
  if SaveDialog1.Execute then
375
  begin
375
  begin
376
    {$REGION 'Switch mutex'}
376
    {$REGION 'Switch mutex'}
377
    hMutexNew := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(SaveDialog1.FileName))));
377
    hMutexNew := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(SaveDialog1.FileName))));
378
    if GetLastError = ERROR_ALREADY_EXISTS then
378
    if GetLastError = ERROR_ALREADY_EXISTS then
379
    begin
379
    begin
380
      ShowMessageFmt(SCannotSaveBecauseMutex, [SaveDialog1.FileName]);
380
      ShowMessageFmt(SCannotSaveBecauseMutex, [SaveDialog1.FileName]);
381
      Close;
381
      Close;
382
    end;
382
    end;
383
 
383
 
384
    if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
384
    if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
385
    hMutex := hMutexNew;
385
    hMutex := hMutexNew;
386
    {$ENDREGION}
386
    {$ENDREGION}
387
 
387
 
388
    FSaveAsFilename := SaveDialog1.FileName;
388
    FSaveAsFilename := SaveDialog1.FileName;
389
    Caption := Copy(Caption, 1, Pos(' - ', Caption)-1) + ' - ' + FSaveAsFilename;
389
    Caption := Copy(Caption, 1, Pos(' - ', Caption)-1) + ' - ' + FSaveAsFilename;
390
    Application.Title := Format('%s - FastPHP', [ExtractFileName(FSaveAsFilename)]); // do not translate!
390
    Application.Title := Format('%s - FastPHP', [ExtractFileName(FSaveAsFilename)]); // do not translate!
391
    Button7.Click;
391
    Button7.Click;
392
  end;
392
  end;
393
end;
393
end;
394
 
394
 
395
procedure TForm1.ActionSaveExecute(Sender: TObject);
395
procedure TForm1.ActionSaveExecute(Sender: TObject);
396
begin
396
begin
397
  RightTrimAll;
397
  RightTrimAll;
398
  SaveToFile(GetScrapFile);
398
  SaveToFile(GetScrapFile);
399
  SynEdit1.Modified := false;
399
  SynEdit1.Modified := false;
400
  RefreshModifySign;
400
  RefreshModifySign;
401
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
401
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
402
end;
402
end;
403
 
403
 
404
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
404
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
405
 
405
 
406
    function SpacesAtBeginning(line: string): integer;
406
    function SpacesAtBeginning(line: string): integer;
407
    begin
407
    begin
408
      result := 0;
408
      result := 0;
409
      if Trim(line) = '' then exit;
409
      if Trim(line) = '' then exit;
410
      while line[result+1] = ' ' do
410
      while line[result+1] = ' ' do
411
      begin
411
      begin
412
        inc(result);
412
        inc(result);
413
      end;
413
      end;
414
    end;
414
    end;
415
 
415
 
416
    function GuessIndent(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): integer;
416
    function GuessIndent(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): integer;
417
      function _Check(indent: integer): boolean;
417
      function _Check(indent: integer): boolean;
418
      var
418
      var
419
        i: integer;
419
        i: integer;
420
      begin
420
      begin
421
        result := true;
421
        result := true;
422
        for i := 0 to lines.Count-1 do
422
        for i := 0 to lines.Count-1 do
423
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
423
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
424
          begin
424
          begin
425
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
425
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
426
            result := false;
426
            result := false;
427
            exit;
427
            exit;
428
          end;
428
          end;
429
      end;
429
      end;
430
    var
430
    var
431
      i: integer;
431
      i: integer;
432
    begin
432
    begin
433
      for i := 8 downto 2 do
433
      for i := 8 downto 2 do
434
      begin
434
      begin
435
        if _Check(i) then
435
        if _Check(i) then
436
        begin
436
        begin
437
          result := i;
437
          result := i;
438
          exit;
438
          exit;
439
        end;
439
        end;
440
      end;
440
      end;
441
      result := -1;
441
      result := -1;
442
    end;
442
    end;
443
 
443
 
444
    procedure SpaceToTab(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}; indent: integer);
444
    procedure SpaceToTab(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}; indent: integer);
445
    var
445
    var
446
      i, spaces: integer;
446
      i, spaces: integer;
447
      newval: string;
447
      newval: string;
448
      somethingchanged: boolean;
448
      somethingchanged: boolean;
449
    begin
449
    begin
450
      somethingchanged := false;
450
      somethingchanged := false;
451
      for i := 0 to lines.Count-1 do
451
      for i := 0 to lines.Count-1 do
452
      begin
452
      begin
453
        spaces := SpacesAtBeginning(lines.Strings[i]);
453
        spaces := SpacesAtBeginning(lines.Strings[i]);
454
        newval := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
454
        newval := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
455
        if lines.Strings[i] <> newval then
455
        if lines.Strings[i] <> newval then
456
        begin
456
        begin
457
          somethingchanged := true;
457
          somethingchanged := true;
458
          lines.Strings[i] := newval;
458
          lines.Strings[i] := newval;
459
        end;
459
        end;
460
      end;
460
      end;
461
      if somethingchanged then
461
      if somethingchanged then
462
      begin
462
      begin
-
 
463
        SynEdit1.Modified := true; // seems to be required by Delphi 10.x
463
        RefreshModifySign;
464
        RefreshModifySign;
464
      end;
465
      end;
465
    end;
466
    end;
466
 
467
 
467
    function SpacesAvailable(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): boolean;
468
    function SpacesAvailable(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): boolean;
468
    var
469
    var
469
      i, spaces: integer;
470
      i, spaces: integer;
470
    begin
471
    begin
471
      for i := 0 to lines.Count-1 do
472
      for i := 0 to lines.Count-1 do
472
      begin
473
      begin
473
        spaces := SpacesAtBeginning(lines.Strings[i]);
474
        spaces := SpacesAtBeginning(lines.Strings[i]);
474
        if spaces > 0 then
475
        if spaces > 0 then
475
        begin
476
        begin
476
          result := true;
477
          result := true;
477
          exit;
478
          exit;
478
        end;
479
        end;
479
      end;
480
      end;
480
      result := false;
481
      result := false;
481
      exit;
482
      exit;
482
    end;
483
    end;
483
 
484
 
484
var
485
var
485
  val: string;
486
  val: string;
486
  ind: integer;
487
  ind: integer;
487
resourcestring
488
resourcestring
488
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
489
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
489
  SSpacesToLabs = 'Spaces to tabs';
490
  SSpacesToLabs = 'Spaces to tabs';
490
  SIndent = 'Indent:';
491
  SIndent = 'Indent:';
491
begin
492
begin
492
  // TODO: if something is selected, only process the selected part
493
  // TODO: if something is selected, only process the selected part
493
 
494
 
494
  if not SpacesAvailable(SynEdit1.Lines) then
495
  if not SpacesAvailable(SynEdit1.Lines) then
495
  begin
496
  begin
496
    MessageDlg(SNoLinesAvailable, mtInformation, [mbOk], 0);
497
    MessageDlg(SNoLinesAvailable, mtInformation, [mbOk], 0);
497
    exit;
498
    exit;
498
  end;
499
  end;
499
 
500
 
500
  ind := GuessIndent(SynEdit1.Lines);
501
  ind := GuessIndent(SynEdit1.Lines);
501
  if ind <> -1 then val := IntToStr(ind);
502
  if ind <> -1 then val := IntToStr(ind);
502
 
503
 
503
  if not InputQuery(SSpacesToLabs, SIndent, val) or
504
  if not InputQuery(SSpacesToLabs, SIndent, val) or
504
     not TryStrToInt(Trim(val), ind) then
505
     not TryStrToInt(Trim(val), ind) then
505
  begin
506
  begin
506
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
507
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
507
    exit;
508
    exit;
508
  end;
509
  end;
509
 
510
 
510
  if ind = 0 then exit;
511
  if ind = 0 then exit;
511
  SpaceToTab(SynEdit1.Lines, ind);
512
  SpaceToTab(SynEdit1.Lines, ind);
512
end;
513
end;
513
 
514
 
514
procedure TForm1.ActionESCExecute(Sender: TObject);
515
procedure TForm1.ActionESCExecute(Sender: TObject);
515
begin
516
begin
516
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
517
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
517
     (HelpTabsheet.TabVisible) then
518
     (HelpTabsheet.TabVisible) then
518
  begin
519
  begin
519
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
520
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
520
    HelpTabsheet.TabVisible := false;
521
    HelpTabsheet.TabVisible := false;
521
  end;
522
  end;
522
 
523
 
523
  // Dirty hack...
524
  // Dirty hack...
524
  SrcRep.CloseDialogs;
525
  SrcRep.CloseDialogs;
525
end;
526
end;
526
 
527
 
527
procedure TForm1.ActionFindExecute(Sender: TObject);
528
procedure TForm1.ActionFindExecute(Sender: TObject);
528
begin
529
begin
529
  SrcRep.FindExecute;
530
  SrcRep.FindExecute;
530
end;
531
end;
531
 
532
 
532
procedure TForm1.Run(Sender: TObject);
533
procedure TForm1.Run(Sender: TObject);
533
var
534
var
534
  bakTS: TTabSheet;
535
  bakTS: TTabSheet;
535
  //ss: TStringStream;
536
  //ss: TStringStream;
536
  //bakPos: Int64;
537
  //bakPos: Int64;
537
begin
538
begin
538
  memo2.Lines.Text := '';
539
  memo2.Lines.Text := '';
539
 
540
 
540
  if not BrowserLoadedOnce then
541
  if not BrowserLoadedOnce then
541
  begin
542
  begin
542
    bakTS := PageControl1.ActivePage;
543
    bakTS := PageControl1.ActivePage;
543
    try
544
    try
544
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
545
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
545
      Webbrowser1.Clear;
546
      Webbrowser1.Clear;
546
    finally
547
    finally
547
      PageControl1.ActivePage := bakTS;
548
      PageControl1.ActivePage := bakTS;
548
    end;
549
    end;
549
    BrowserLoadedOnce := true;
550
    BrowserLoadedOnce := true;
550
  end
551
  end
551
  else
552
  else
552
    Webbrowser1.Clear;
553
    Webbrowser1.Clear;
553
 
554
 
554
  Screen.Cursor := crHourGlass; // TODO: Doesn't work with F9
555
  Screen.Cursor := crHourGlass; // TODO: Doesn't work with F9
555
  Application.ProcessMessages;
556
  Application.ProcessMessages;
556
 
557
 
557
  try
558
  try
558
    ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
559
    ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
559
 
560
 
560
    // TODO 70421 * <fastphp> implement flush() with ContentCallBack implementieren... For long running scripts I want to see status changes via javascript which are loaded step by step
561
    // TODO 70421 * <fastphp> implement flush() with ContentCallBack implementieren... For long running scripts I want to see status changes via javascript which are loaded step by step
561
    // TODO 70422 * <fastphp> when a script has an endless loop, i want to have a possibility to cancel it
562
    // TODO 70422 * <fastphp> when a script has an endless loop, i want to have a possibility to cancel it
562
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
563
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
563
      memo2.Lines.Text := Utf8Decode(RunPHPScript(GetScrapFile, Sender=ActionLint, False)) // if we have a UTF-8 file, then the DOS output is double-UTF8 encoded
564
      memo2.Lines.Text := Utf8Decode(RunPHPScript(GetScrapFile, Sender=ActionLint, False)) // if we have a UTF-8 file, then the DOS output is double-UTF8 encoded
564
    else
565
    else
565
      memo2.Lines.Text := RunPHPScript(GetScrapFile, Sender=ActionLint, False);
566
      memo2.Lines.Text := RunPHPScript(GetScrapFile, Sender=ActionLint, False);
566
 
567
 
567
    {$REGION 'Show in Web Browser'}
568
    {$REGION 'Show in Web Browser'}
568
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
569
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
569
      Webbrowser1.LoadHTML(MarkUpLineReference('<meta charset="utf-8">'+memo2.Lines.Text), GetScrapFile)
570
      Webbrowser1.LoadHTML(MarkUpLineReference('<meta charset="utf-8">'+memo2.Lines.Text), GetScrapFile)
570
    else
571
    else
571
      Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
572
      Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
572
 
573
 
573
    // Alternatively:
574
    // Alternatively:
574
    (*
575
    (*
575
    ss := TstringStream.Create;
576
    ss := TstringStream.Create;
576
    ss.WriteString(MarkUpLineReference(memo2.Lines.Text));
577
    ss.WriteString(MarkUpLineReference(memo2.Lines.Text));
577
    ss.Position := 0;
578
    ss.Position := 0;
578
    Webbrowser1.LoadStream(ss, GetScrapFile);
579
    Webbrowser1.LoadStream(ss, GetScrapFile);
579
    Webbrowser1.Wait;
580
    Webbrowser1.Wait;
580
    ss.Free;
581
    ss.Free;
581
    *)
582
    *)
582
    {$ENDREGION}
583
    {$ENDREGION}
583
 
584
 
584
    if IsTextHTML(memo2.lines.text) then
585
    if IsTextHTML(memo2.lines.text) then
585
      PageControl1.ActivePage := HtmlTabSheet
586
      PageControl1.ActivePage := HtmlTabSheet
586
    else
587
    else
587
      PageControl1.ActivePage := PlaintextTabSheet;
588
      PageControl1.ActivePage := PlaintextTabSheet;
588
  finally
589
  finally
589
    Screen.Cursor := crDefault;
590
    Screen.Cursor := crDefault;
590
  end;
591
  end;
591
end;
592
end;
592
 
593
 
593
procedure TForm1.RunConsole(Sender: TObject);
594
procedure TForm1.RunConsole(Sender: TObject);
594
begin
595
begin
595
  ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
596
  ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
596
  RunPHPScript(GetScrapFile, Sender=ActionLint, True);
597
  RunPHPScript(GetScrapFile, Sender=ActionLint, True);
597
end;
598
end;
598
 
599
 
599
procedure TForm1.SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
600
procedure TForm1.SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
600
  AFiles: TStrings);
601
  AFiles: TStrings);
601
var
602
var
602
  FileName: string;
603
  FileName: string;
603
const
604
const
604
  WARN_FILE_COUNT = 10;
605
  WARN_FILE_COUNT = 10;
605
resourcestring
606
resourcestring
606
  SAreYouSure = 'Are you sure you want to open %d files?';
607
  SAreYouSure = 'Are you sure you want to open %d files?';
607
begin
608
begin
608
  if AFiles.Count > WARN_FILE_COUNT then
609
  if AFiles.Count > WARN_FILE_COUNT then
609
  begin
610
  begin
610
    if not MessageDlg(Format(SAreYouSure, [WARN_FILE_COUNT]), mtConfirmation, mbYesNoCancel, 0) <> mrYes then
611
    if not MessageDlg(Format(SAreYouSure, [WARN_FILE_COUNT]), mtConfirmation, mbYesNoCancel, 0) <> mrYes then
611
      exit;
612
      exit;
612
  end;
613
  end;
613
 
614
 
614
  for FileName in AFiles do
615
  for FileName in AFiles do
615
  begin
616
  begin
616
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + FileName + '"'), '', SW_NORMAL);
617
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + FileName + '"'), '', SW_NORMAL);
617
  end;
618
  end;
618
end;
619
end;
619
 
620
 
620
procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
621
procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
621
  Y, Line: Integer; Mark: TSynEditMark);
622
  Y, Line: Integer; Mark: TSynEditMark);
622
begin
623
begin
623
  (*
624
  (*
624
  TSynEdit(Sender).CaretX := 1;
625
  TSynEdit(Sender).CaretX := 1;
625
  TSynEdit(Sender).CaretY := Line;
626
  TSynEdit(Sender).CaretY := Line;
626
  TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
627
  TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
627
  *)
628
  *)
628
end;
629
end;
629
 
630
 
630
procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
631
procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
631
  Shift: TShiftState);
632
  Shift: TShiftState);
632
begin
633
begin
633
  if (Shift = [ssCtrl, ssShift]) and (Key = ord('C')) then
634
  if (Shift = [ssCtrl, ssShift]) and (Key = ord('C')) then
634
  begin
635
  begin
635
    // Disable "Column Select Mode" (https://github.com/SynEdit/SynEdit/blob/master/Source/SynEditKeyCmds.pas#L879)
636
    // Disable "Column Select Mode" (https://github.com/SynEdit/SynEdit/blob/master/Source/SynEditKeyCmds.pas#L879)
636
    // which can be enabled by pressing Ctrl+Shift+C
637
    // which can be enabled by pressing Ctrl+Shift+C
637
    // Reasons why we disable it:
638
    // Reasons why we disable it:
638
    // 1. I think nobody needs this, and sometimes you accidentally press it
639
    // 1. I think nobody needs this, and sometimes you accidentally press it
639
    // 2. Ctrl+Shift+L would the combination to disable column select mode,
640
    // 2. Ctrl+Shift+L would the combination to disable column select mode,
640
    //    but you cannot use it, because it is already in use for linting
641
    //    but you cannot use it, because it is already in use for linting
641
    Key := 0;
642
    Key := 0;
642
  end;
643
  end;
643
end;
644
end;
644
 
645
 
645
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
646
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
646
{$IFDEF OnlineHelp}
647
{$IFDEF OnlineHelp}
647
var
648
var
648
  Line: Integer;
649
  Line: Integer;
649
  Column: Integer;
650
  Column: Integer;
650
  word: string;
651
  word: string;
651
begin
652
begin
652
  Line  := aLineCharPos.Line-1;
653
  Line  := aLineCharPos.Line-1;
653
  Column := aLineCharPos.Char-1;
654
  Column := aLineCharPos.Char-1;
654
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
655
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
655
  if word <> gOnlineHelpWord then
656
  if word <> gOnlineHelpWord then
656
  begin
657
  begin
657
    gOnlineHelpWord := word;
658
    gOnlineHelpWord := word;
658
    Timer1.Enabled := false;
659
    Timer1.Enabled := false;
659
    Timer1.Enabled := true;
660
    Timer1.Enabled := true;
660
  end;
661
  end;
661
{$ELSE}
662
{$ELSE}
662
begin
663
begin
663
{$ENDIF}
664
{$ENDIF}
664
end;
665
end;
665
 
666
 
666
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
667
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
667
  MousePos: TPoint; var Handled: Boolean);
668
  MousePos: TPoint; var Handled: Boolean);
668
begin
669
begin
669
  if ssCtrl in Shift then
670
  if ssCtrl in Shift then
670
  begin
671
  begin
671
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
672
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
672
    Handled := true;
673
    Handled := true;
673
  end
674
  end
674
  else Handled := false;
675
  else Handled := false;
675
end;
676
end;
676
 
677
 
677
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
678
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
678
  MousePos: TPoint; var Handled: Boolean);
679
  MousePos: TPoint; var Handled: Boolean);
679
begin
680
begin
680
  if ssCtrl in Shift then
681
  if ssCtrl in Shift then
681
  begin
682
  begin
682
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
683
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
683
    Handled := true;
684
    Handled := true;
684
  end
685
  end
685
  else Handled := false;
686
  else Handled := false;
686
end;
687
end;
687
 
688
 
688
procedure TForm1.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType);
689
procedure TForm1.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType);
689
var
690
var
690
  Editor: TSynEdit;
691
  Editor: TSynEdit;
691
  OpenChars: array of WideChar;//[0..2] of WideChar=();
692
  OpenChars: array of WideChar;//[0..2] of WideChar=();
692
  CloseChars: array of WideChar;//[0..2] of WideChar=();
693
  CloseChars: array of WideChar;//[0..2] of WideChar=();
693
 
694
 
694
  function IsCharBracket(AChar: WideChar): Boolean;
695
  function IsCharBracket(AChar: WideChar): Boolean;
695
  begin
696
  begin
696
    case AChar of
697
    case AChar of
697
      '{','[','(','<','}',']',')','>':
698
      '{','[','(','<','}',']',')','>':
698
        Result := True;
699
        Result := True;
699
      else
700
      else
700
        Result := False;
701
        Result := False;
701
    end;
702
    end;
702
  end;
703
  end;
703
 
704
 
704
  function CharToPixels(P: TBufferCoord): TPoint;
705
  function CharToPixels(P: TBufferCoord): TPoint;
705
  begin
706
  begin
706
    Result := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(P));
707
    Result := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(P));
707
  end;
708
  end;
708
 
709
 
709
var
710
var
710
  COLOR_FG: TColor;
711
  COLOR_FG: TColor;
711
  COLOR_BG: TColor;
712
  COLOR_BG: TColor;
712
  P: TBufferCoord;
713
  P: TBufferCoord;
713
  Pix: TPoint;
714
  Pix: TPoint;
714
  D: TDisplayCoord;
715
  D: TDisplayCoord;
715
  S: UnicodeString;
716
  S: UnicodeString;
716
  I: Integer;
717
  I: Integer;
717
  Attri: TSynHighlighterAttributes;
718
  Attri: TSynHighlighterAttributes;
718
  ArrayLength: Integer;
719
  ArrayLength: Integer;
719
  start: Integer;
720
  start: Integer;
720
  TmpCharA, TmpCharB: WideChar;
721
  TmpCharA, TmpCharB: WideChar;
721
begin
722
begin
722
  // Source: https://github.com/SynEdit/SynEdit/blob/master/Demos/OnPaintTransientDemo/Unit1.pas
723
  // Source: https://github.com/SynEdit/SynEdit/blob/master/Demos/OnPaintTransientDemo/Unit1.pas
723
 
724
 
724
  if IsThemeDark then
725
  if IsThemeDark then
725
  begin
726
  begin
726
    COLOR_FG := clLime;
727
    COLOR_FG := clLime;
727
    COLOR_BG := clGreen;
728
    COLOR_BG := clGreen;
728
  end
729
  end
729
  else
730
  else
730
  begin
731
  begin
731
    COLOR_FG := clGreen;
732
    COLOR_FG := clGreen;
732
    COLOR_BG := clLime;
733
    COLOR_BG := clLime;
733
  end;
734
  end;
734
 
735
 
735
  if TSynEdit(Sender).SelAvail then exit;
736
  if TSynEdit(Sender).SelAvail then exit;
736
  Editor := TSynEdit(Sender);
737
  Editor := TSynEdit(Sender);
737
  ArrayLength:= 3;
738
  ArrayLength:= 3;
738
 
739
 
739
  (*
740
  (*
740
  if (Editor.Highlighter = shHTML) or (Editor.Highlighter = shXML) then
741
  if (Editor.Highlighter = shHTML) or (Editor.Highlighter = shXML) then
741
    inc(ArrayLength);
742
    inc(ArrayLength);
742
  *)
743
  *)
743
 
744
 
744
  SetLength(OpenChars, ArrayLength);
745
  SetLength(OpenChars, ArrayLength);
745
  SetLength(CloseChars, ArrayLength);
746
  SetLength(CloseChars, ArrayLength);
746
  for i := 0 to ArrayLength - 1 do
747
  for i := 0 to ArrayLength - 1 do
747
  begin
748
  begin
748
    case i of
749
    case i of
749
      0: begin OpenChars[i] := '('; CloseChars[i] := ')'; end;
750
      0: begin OpenChars[i] := '('; CloseChars[i] := ')'; end;
750
      1: begin OpenChars[i] := '{'; CloseChars[i] := '}'; end;
751
      1: begin OpenChars[i] := '{'; CloseChars[i] := '}'; end;
751
      2: begin OpenChars[i] := '['; CloseChars[i] := ']'; end;
752
      2: begin OpenChars[i] := '['; CloseChars[i] := ']'; end;
752
      3: begin OpenChars[i] := '<'; CloseChars[i] := '>'; end;
753
      3: begin OpenChars[i] := '<'; CloseChars[i] := '>'; end;
753
    end;
754
    end;
754
  end;
755
  end;
755
 
756
 
756
  P := Editor.CaretXY;
757
  P := Editor.CaretXY;
757
  D := Editor.DisplayXY;
758
  D := Editor.DisplayXY;
758
 
759
 
759
  Start := Editor.SelStart;
760
  Start := Editor.SelStart;
760
 
761
 
761
  if (Start > 0) and (Start <= length(Editor.Text)) then
762
  if (Start > 0) and (Start <= length(Editor.Text)) then
762
    TmpCharA := Editor.Text[Start]
763
    TmpCharA := Editor.Text[Start]
763
  else
764
  else
764
    TmpCharA := #0;
765
    TmpCharA := #0;
765
 
766
 
766
  if (Start > 0){Added by VTS} and (Start < length(Editor.Text)) then
767
  if (Start > 0){Added by VTS} and (Start < length(Editor.Text)) then
767
    TmpCharB := Editor.Text[Start + 1]
768
    TmpCharB := Editor.Text[Start + 1]
768
  else
769
  else
769
    TmpCharB := #0;
770
    TmpCharB := #0;
770
 
771
 
771
  if not IsCharBracket(TmpCharA) and not IsCharBracket(TmpCharB) then exit;
772
  if not IsCharBracket(TmpCharA) and not IsCharBracket(TmpCharB) then exit;
772
  S := TmpCharB;
773
  S := TmpCharB;
773
  if not IsCharBracket(TmpCharB) then
774
  if not IsCharBracket(TmpCharB) then
774
  begin
775
  begin
775
    P.Char := P.Char - 1;
776
    P.Char := P.Char - 1;
776
    S := TmpCharA;
777
    S := TmpCharA;
777
  end;
778
  end;
778
  Editor.GetHighlighterAttriAtRowCol(P, S, Attri);
779
  Editor.GetHighlighterAttriAtRowCol(P, S, Attri);
779
 
780
 
780
  if (Editor.Highlighter.SymbolAttribute = Attri) then
781
  if (Editor.Highlighter.SymbolAttribute = Attri) then
781
  begin
782
  begin
782
    for i := low(OpenChars) to High(OpenChars) do
783
    for i := low(OpenChars) to High(OpenChars) do
783
    begin
784
    begin
784
      if (S = OpenChars[i]) or (S = CloseChars[i]) then
785
      if (S = OpenChars[i]) or (S = CloseChars[i]) then
785
      begin
786
      begin
786
        Pix := CharToPixels(P);
787
        Pix := CharToPixels(P);
787
 
788
 
788
        Editor.Canvas.Brush.Style := bsSolid;//Clear;
789
        Editor.Canvas.Brush.Style := bsSolid;//Clear;
789
        Editor.Canvas.Font.Assign(Editor.Font);
790
        Editor.Canvas.Font.Assign(Editor.Font);
790
        Editor.Canvas.Font.Style := Attri.Style;
791
        Editor.Canvas.Font.Style := Attri.Style;
791
 
792
 
792
        if (TransientType = ttAfter) then
793
        if (TransientType = ttAfter) then
793
        begin
794
        begin
794
          Editor.Canvas.Font.Color := COLOR_FG;
795
          Editor.Canvas.Font.Color := COLOR_FG;
795
          Editor.Canvas.Brush.Color := COLOR_BG;
796
          Editor.Canvas.Brush.Color := COLOR_BG;
796
        end
797
        end
797
        else
798
        else
798
        begin
799
        begin
799
          Editor.Canvas.Font.Color := Attri.Foreground;
800
          Editor.Canvas.Font.Color := Attri.Foreground;
800
          Editor.Canvas.Brush.Color := Attri.Background;
801
          Editor.Canvas.Brush.Color := Attri.Background;
801
        end;
802
        end;
802
        if Editor.Canvas.Font.Color = clNone then
803
        if Editor.Canvas.Font.Color = clNone then
803
          Editor.Canvas.Font.Color := Editor.Font.Color;
804
          Editor.Canvas.Font.Color := Editor.Font.Color;
804
        if Editor.Canvas.Brush.Color = clNone then
805
        if Editor.Canvas.Brush.Color = clNone then
805
          Editor.Canvas.Brush.Color := Editor.Color;
806
          Editor.Canvas.Brush.Color := Editor.Color;
806
 
807
 
807
        Editor.Canvas.TextOut(Pix.X, Pix.Y, S);
808
        Editor.Canvas.TextOut(Pix.X, Pix.Y, S);
808
        P := Editor.GetMatchingBracketEx(P);
809
        P := Editor.GetMatchingBracketEx(P);
809
 
810
 
810
        if (P.Char > 0) and (P.Line > 0) then
811
        if (P.Char > 0) and (P.Line > 0) then
811
        begin
812
        begin
812
          Pix := CharToPixels(P);
813
          Pix := CharToPixels(P);
-
 
814
          {$IF CompilerVersion >= 35.0} // Added by ViaThinkSoft (not sure about the exact version)
-
 
815
          if Pix.X > Editor.Gutter.RealGutterWidth then // Added by ViaThinkSoft (not sure about the exact version)
-
 
816
          {$ELSE}
813
          if Pix.X > Editor.Gutter.Width then
817
          if Pix.X > Editor.Gutter.Width then
-
 
818
          {$IFEND}
814
          begin
819
          begin
815
            {$REGION 'Added by ViaThinkSoft'}
820
            {$REGION 'Added by ViaThinkSoft'}
816
            if (TransientType = ttAfter) then
821
            if (TransientType = ttAfter) then
817
            begin
822
            begin
818
              Editor.Canvas.Font.Color := COLOR_FG;
823
              Editor.Canvas.Font.Color := COLOR_FG;
819
              Editor.Canvas.Brush.Color := COLOR_BG;
824
              Editor.Canvas.Brush.Color := COLOR_BG;
820
            end
825
            end
821
            else
826
            else
822
            begin
827
            begin
823
              Editor.Canvas.Font.Color := Attri.Foreground;
828
              Editor.Canvas.Font.Color := Attri.Foreground;
824
              Editor.Canvas.Brush.Color := Attri.Background;
829
              Editor.Canvas.Brush.Color := Attri.Background;
825
            end;
830
            end;
826
            if Editor.Canvas.Font.Color = clNone then
831
            if Editor.Canvas.Font.Color = clNone then
827
              Editor.Canvas.Font.Color := Editor.Font.Color;
832
              Editor.Canvas.Font.Color := Editor.Font.Color;
828
            if Editor.Canvas.Brush.Color = clNone then
833
            if Editor.Canvas.Brush.Color = clNone then
829
              Editor.Canvas.Brush.Color := Editor.Color;
834
              Editor.Canvas.Brush.Color := Editor.Color;
830
            {$ENDREGION}
835
            {$ENDREGION}
831
            if S = OpenChars[i] then
836
            if S = OpenChars[i] then
832
              Editor.Canvas.TextOut(Pix.X, Pix.Y, CloseChars[i])
837
              Editor.Canvas.TextOut(Pix.X, Pix.Y, CloseChars[i])
833
            else Editor.Canvas.TextOut(Pix.X, Pix.Y, OpenChars[i]);
838
            else Editor.Canvas.TextOut(Pix.X, Pix.Y, OpenChars[i]);
834
          end;
839
          end;
835
        end;
840
        end;
836
      end;
841
      end;
837
    end;
842
    end;
838
    Editor.Canvas.Brush.Style := bsSolid;
843
    Editor.Canvas.Brush.Style := bsSolid;
839
  end;
844
  end;
840
end;
845
end;
841
 
846
 
842
procedure TForm1.SynEdit1StatusChange(Sender: TObject;
847
procedure TForm1.SynEdit1StatusChange(Sender: TObject;
843
  Changes: TSynStatusChanges);
848
  Changes: TSynStatusChanges);
844
begin
849
begin
845
  if scModified in Changes then
850
  if scModified in Changes then
846
    RefreshModifySign;
851
    RefreshModifySign;
847
end;
852
end;
848
 
853
 
849
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
854
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
850
begin
855
begin
851
  SynEditFocusTimer.Enabled := false;
856
  SynEditFocusTimer.Enabled := false;
852
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
857
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
853
  SynEdit1.SetFocus;
858
  SynEdit1.SetFocus;
854
end;
859
end;
855
 
860
 
856
procedure TForm1.Theme_Dark;
861
procedure TForm1.Theme_Dark;
857
begin
862
begin
858
  if IsThemeDark then exit;
863
  if IsThemeDark then exit;
859
  TStyleManager.TrySetStyle('Windows10 SlateGray'); // do not translate
864
  TStyleManager.TrySetStyle('Windows10 SlateGray'); // do not translate
860
  Color := 1316887;
865
  Color := 1316887;
861
  Font.Color := clCream;
866
  Font.Color := clCream;
862
  //Memo2.Font.Color := clCream;
867
  //Memo2.Font.Color := clCream;
863
  //Memo2.ParentColor := true;
868
  //Memo2.ParentColor := true;
864
  SynEdit1.ActiveLineColor := 2238502;
869
  SynEdit1.ActiveLineColor := 2238502;
865
  SynEdit1.Color := 1316887;
870
  SynEdit1.Color := 1316887;
866
  SynEdit1.Font.Color := clCream;
871
  SynEdit1.Font.Color := clCream;
867
  SynEdit1.Gutter.Color := 1316887;
872
  SynEdit1.Gutter.Color := 1316887;
868
  SynEdit1.Gutter.Font.Color := clCream;
873
  SynEdit1.Gutter.Font.Color := clCream;
869
  SynEdit1.Gutter.GradientStartColor := 2238502;
874
  SynEdit1.Gutter.GradientStartColor := 2238502;
870
  SynEdit1.Gutter.GradientEndColor := 1316887;
875
  SynEdit1.Gutter.GradientEndColor := 1316887;
871
  SynPHPSyn1.CommentAttri.Foreground := $00837B82;
876
  SynPHPSyn1.CommentAttri.Foreground := $00837B82;
872
  SynPHPSyn1.IdentifierAttri.Foreground := 9627120;
877
  SynPHPSyn1.IdentifierAttri.Foreground := 9627120;
873
  SynPHPSyn1.KeyAttri.Foreground := 4157595;
878
  SynPHPSyn1.KeyAttri.Foreground := 4157595;
874
  SynPHPSyn1.NumberAttri.Foreground := 5008079;
879
  SynPHPSyn1.NumberAttri.Foreground := 5008079;
875
  SynPHPSyn1.StringAttri.Foreground := 6987151;
880
  SynPHPSyn1.StringAttri.Foreground := 6987151;
876
  SynPHPSyn1.SymbolAttri.Foreground := 8769754;
881
  SynPHPSyn1.SymbolAttri.Foreground := 8769754;
877
  SynPHPSyn1.VariableAttri.Foreground := 6924493;
882
  SynPHPSyn1.VariableAttri.Foreground := 6924493;
878
end;
883
end;
879
 
884
 
880
procedure TForm1.Theme_Light;
885
procedure TForm1.Theme_Light;
881
begin
886
begin
882
  if not IsThemeDark then exit;
887
  if not IsThemeDark then exit;
883
  TStyleManager.TrySetStyle('Windows'); // do not translate
888
  TStyleManager.TrySetStyle('Windows'); // do not translate
884
  Color := clBtnFace;
889
  Color := clBtnFace;
885
  Font.Color := clWindowText;
890
  Font.Color := clWindowText;
886
  //Memo2.Font.Color := clWindowText;
891
  //Memo2.Font.Color := clWindowText;
887
  SynEdit1.ActiveLineColor := 14680010;
892
  SynEdit1.ActiveLineColor := 14680010;
888
  SynEdit1.Color := clCream;
893
  SynEdit1.Color := clCream;
889
  SynEdit1.Font.Color := clWindowText;
894
  SynEdit1.Font.Color := clWindowText;
890
  SynEdit1.Gutter.Color := clBtnFace;
895
  SynEdit1.Gutter.Color := clBtnFace;
891
  SynEdit1.Gutter.Font.Color := clWindowText;
896
  SynEdit1.Gutter.Font.Color := clWindowText;
892
  SynEdit1.Gutter.GradientStartcolor := cl3dLight;
897
  SynEdit1.Gutter.GradientStartcolor := cl3dLight;
893
  SynEdit1.Gutter.GradientEndColor := clBtnFace;;
898
  SynEdit1.Gutter.GradientEndColor := clBtnFace;;
894
  SynPHPSyn1.CommentAttri.Foreground := 33023;
899
  SynPHPSyn1.CommentAttri.Foreground := 33023;
895
  SynPHPSyn1.IdentifierAttri.Foreground := 4194304;
900
  SynPHPSyn1.IdentifierAttri.Foreground := 4194304;
896
  SynPHPSyn1.KeyAttri.Foreground := 4227072;
901
  SynPHPSyn1.KeyAttri.Foreground := 4227072;
897
  SynPHPSyn1.NumberAttri.Foreground := 213;
902
  SynPHPSyn1.NumberAttri.Foreground := 213;
898
  SynPHPSyn1.StringAttri.Foreground := 13762560;
903
  SynPHPSyn1.StringAttri.Foreground := 13762560;
899
  SynPHPSyn1.SymbolAttri.Foreground := 4227072;
904
  SynPHPSyn1.SymbolAttri.Foreground := 4227072;
900
  SynPHPSyn1.VariableAttri.Foreground := 213;
905
  SynPHPSyn1.VariableAttri.Foreground := 213;
901
end;
906
end;
902
 
907
 
903
procedure TForm1.Timer1Timer(Sender: TObject);
908
procedure TForm1.Timer1Timer(Sender: TObject);
904
begin
909
begin
905
  {$IFDEF OnlineHelp}
910
  {$IFDEF OnlineHelp}
906
  Timer1.Enabled := false;
911
  Timer1.Enabled := false;
907
 
912
 
908
  // TODO: Insert a small online help hint
913
  // TODO: Insert a small online help hint
909
  //Caption := gOnlineHelpWord;
914
  //Caption := gOnlineHelpWord;
910
  {$ENDIF}
915
  {$ENDIF}
911
end;
916
end;
912
 
917
 
913
procedure TForm1.TreeView1DblClick(Sender: TObject);
918
procedure TForm1.TreeView1DblClick(Sender: TObject);
914
var
919
var
915
  tn: TTreeNode;
920
  tn: TTreeNode;
916
  lineNo: integer;
921
  lineNo: integer;
917
begin
922
begin
918
  tn := TTreeView(Sender).Selected;
923
  tn := TTreeView(Sender).Selected;
919
  if tn = nil then exit;
924
  if tn = nil then exit;
920
  lineNo := Integer(tn.Data);
925
  lineNo := Integer(tn.Data);
921
  if lineNo > 0 then GotoLineNo(lineNo);
926
  if lineNo > 0 then GotoLineNo(lineNo);
922
end;
927
end;
923
 
928
 
924
(*
929
(*
925
{$IFDEF USE_SHDOCVW_TLB}
930
{$IFDEF USE_SHDOCVW_TLB}
926
*)
931
*)
927
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
932
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
928
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
933
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
929
  Headers: OleVariant; var Cancel: WordBool);
934
  Headers: OleVariant; var Cancel: WordBool);
930
begin
935
begin
931
  BeforeNavigate(URL, Cancel);
936
  BeforeNavigate(URL, Cancel);
932
end;
937
end;
933
(*
938
(*
934
{$ELSE}
939
{$ELSE}
935
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
940
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
936
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
941
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
937
  Headers: OleVariant; var Cancel: WordBool);
942
  Headers: OleVariant; var Cancel: WordBool);
938
begin
943
begin
939
  BeforeNavigate(URL, Cancel);
944
  BeforeNavigate(URL, Cancel);
940
end;
945
end;
941
{$ENDIF}
946
{$ENDIF}
942
*)
947
*)
943
 
948
 
944
procedure TForm1.WebBrowser1WindowClosing(ASender: TObject;
949
procedure TForm1.WebBrowser1WindowClosing(ASender: TObject;
945
  IsChildWindow: WordBool; var Cancel: WordBool);
950
  IsChildWindow: WordBool; var Cancel: WordBool);
946
resourcestring
951
resourcestring
947
  SCloseRequest = 'A script has requested the window to be closed. The window of a standalone script would now close.';
952
  SCloseRequest = 'A script has requested the window to be closed. The window of a standalone script would now close.';
948
begin
953
begin
949
  ShowMessage(SCloseRequest);
954
  ShowMessage(SCloseRequest);
950
  TWebBrowser(ASender).Clear;
955
  TWebBrowser(ASender).Clear;
951
  Cancel := true;
956
  Cancel := true;
952
end;
957
end;
953
 
958
 
954
procedure TForm1.BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
959
procedure TForm1.BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
955
var
960
var
956
  s, myURL: string;
961
  s, myURL: string;
957
  lineno: integer;
962
  lineno: integer;
958
  p: integer;
963
  p: integer;
959
begin
964
begin
960
  {$REGION 'Line number references (PHP errors and warnings)'}
965
  {$REGION 'Line number references (PHP errors and warnings)'}
961
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
966
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
962
  begin
967
  begin
963
    try
968
    try
964
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
969
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
965
      if not TryStrToInt(s, lineno) then exit;
970
      if not TryStrToInt(s, lineno) then exit;
966
      GotoLineNo(lineno);
971
      GotoLineNo(lineno);
967
      SynEditFocusTimer.Enabled := true;
972
      SynEditFocusTimer.Enabled := true;
968
    finally
973
    finally
969
      Cancel := true;
974
      Cancel := true;
970
    end;
975
    end;
971
    Exit;
976
    Exit;
972
  end;
977
  end;
973
  {$ENDREGION}
978
  {$ENDREGION}
974
 
979
 
975
  {$REGION 'Intelligent browser (executes PHP scripts which are clicked in a hyperlink)'}
980
  {$REGION 'Intelligent browser (executes PHP scripts which are clicked in a hyperlink)'}
976
  if URL <> 'about:blank' then
981
  if URL <> 'about:blank' then
977
  begin
982
  begin
978
    myUrl := URL;
983
    myUrl := URL;
979
 
984
 
980
    p := Pos('?', myUrl);
985
    p := Pos('?', myUrl);
981
    if p >= 1 then myURL := copy(myURL, 1, p-1);
986
    if p >= 1 then myURL := copy(myURL, 1, p-1);
982
 
987
 
983
    // TODO: myURL urldecode
988
    // TODO: myURL urldecode
984
    // TODO: maybe we could even open that file in the editor!
989
    // TODO: maybe we could even open that file in the editor!
985
    // TODO: ?parameter=....
990
    // TODO: ?parameter=....
986
 
991
 
987
    if FileExists(myURL) and (EndsText('.php', myURL) or EndsText('.php3', myURL) or EndsText('.php4', myURL) or EndsText('.php5', myURL) or EndsText('.phps', myURL)) then
992
    if FileExists(myURL) and (EndsText('.php', myURL) or EndsText('.php3', myURL) or EndsText('.php4', myURL) or EndsText('.php5', myURL) or EndsText('.phps', myURL)) then
988
    begin
993
    begin
989
      WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
994
      WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
990
      Cancel := true;
995
      Cancel := true;
991
    end;
996
    end;
992
  end;
997
  end;
993
  {$ENDREGION}
998
  {$ENDREGION}
994
end;
999
end;
995
 
1000
 
996
procedure TForm1.BtnLightClick(Sender: TObject);
1001
procedure TForm1.BtnLightClick(Sender: TObject);
997
var
1002
var
998
  CanClose: boolean;
1003
  CanClose: boolean;
999
begin
1004
begin
1000
  FormCloseQuery(Form1, CanClose);
1005
  FormCloseQuery(Form1, CanClose);
1001
  if not CanClose then exit;
1006
  if not CanClose then exit;
1002
 
1007
 
1003
  if IsThemeDark then
1008
  if IsThemeDark then
1004
  begin
1009
  begin
1005
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1010
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1006
    Theme_Light;
1011
    Theme_Light;
1007
    TFastPHPConfig.DarkTheme := false;
1012
    TFastPHPConfig.DarkTheme := false;
1008
  end
1013
  end
1009
  else
1014
  else
1010
  begin
1015
  begin
1011
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1016
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1012
    Theme_Dark;
1017
    Theme_Dark;
1013
    TFastPHPConfig.DarkTheme := true;
1018
    TFastPHPConfig.DarkTheme := true;
1014
  end;
1019
  end;
1015
end;
1020
end;
1016
 
1021
 
1017
procedure TForm1.BtnSpecialCharsClick(Sender: TObject);
1022
procedure TForm1.BtnSpecialCharsClick(Sender: TObject);
1018
var
1023
var
1019
  opts: TSynEditorOptions;
1024
  opts: TSynEditorOptions;
1020
begin
1025
begin
1021
  opts := SynEdit1.Options;
1026
  opts := SynEdit1.Options;
1022
  if eoShowSpecialChars in SynEdit1.Options then
1027
  if eoShowSpecialChars in SynEdit1.Options then
1023
  begin
1028
  begin
1024
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1029
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1025
    Exclude(opts, eoShowSpecialChars);
1030
    Exclude(opts, eoShowSpecialChars);
1026
    TFastPHPConfig.SpecialChars := false;
1031
    TFastPHPConfig.SpecialChars := false;
1027
  end
1032
  end
1028
  else
1033
  else
1029
  begin
1034
  begin
1030
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1035
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1031
    Include(opts, eoShowSpecialChars);
1036
    Include(opts, eoShowSpecialChars);
1032
    TFastPHPConfig.SpecialChars := true;
1037
    TFastPHPConfig.SpecialChars := true;
1033
  end;
1038
  end;
1034
  SynEdit1.Options := opts;
1039
  SynEdit1.Options := opts;
1035
end;
1040
end;
1036
 
1041
 
1037
procedure TForm1.FileModTimerTimer(Sender: TObject);
1042
procedure TForm1.FileModTimerTimer(Sender: TObject);
1038
resourcestring
1043
resourcestring
1039
  SChangeConflict = 'The file was changed in a different application BUT IT WAS ALSO MODIFIED HERE! Reload file AND LOSE CHANGES HERE?';
1044
  SChangeConflict = 'The file was changed in a different application BUT IT WAS ALSO MODIFIED HERE! Reload file AND LOSE CHANGES HERE?';
1040
  SChangeNotify = 'The file was changed in a different application! Reload file?';
1045
  SChangeNotify = 'The file was changed in a different application! Reload file?';
1041
begin
1046
begin
1042
  FileModTimer.Enabled := false;
1047
  FileModTimer.Enabled := false;
1043
  if FileModLast <> FileAge(GetScrapFile) then
1048
  if FileModLast <> FileAge(GetScrapFile) then
1044
  begin
1049
  begin
1045
    FileModLast := FileAge(GetScrapFile);
1050
    FileModLast := FileAge(GetScrapFile);
1046
    if SynEdit1.Modified then
1051
    if SynEdit1.Modified then
1047
    begin
1052
    begin
1048
      if MessageDlg(SChangeConflict, mtWarning, mbYesNoCancel, 0) = mrYes then
1053
      if MessageDlg(SChangeConflict, mtWarning, mbYesNoCancel, 0) = mrYes then
1049
      begin
1054
      begin
1050
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1055
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1051
      end;
1056
      end;
1052
    end
1057
    end
1053
    else
1058
    else
1054
    begin
1059
    begin
1055
      if MessageDlg(SChangeNotify, mtConfirmation, mbYesNoCancel, 0) = mrYes then
1060
      if MessageDlg(SChangeNotify, mtConfirmation, mbYesNoCancel, 0) = mrYes then
1056
      begin
1061
      begin
1057
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1062
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1058
      end;
1063
      end;
1059
    end;
1064
    end;
1060
  end;
1065
  end;
1061
  FileModTimer.Enabled := true;
1066
  FileModTimer.Enabled := true;
1062
end;
1067
end;
1063
 
1068
 
1064
procedure TForm1.FontSizeTimerTimer(Sender: TObject);
1069
procedure TForm1.FontSizeTimerTimer(Sender: TObject);
1065
begin
1070
begin
1066
  if Memo2.Font.Size <> (SynEdit1.Font.Size-3) then
1071
  if Memo2.Font.Size <> (SynEdit1.Font.Size-3) then
1067
    Memo2.Font.Size := (SynEdit1.Font.Size-3);
1072
    Memo2.Font.Size := (SynEdit1.Font.Size-3);
1068
end;
1073
end;
1069
 
1074
 
1070
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
1075
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
1071
begin
1076
begin
1072
  DragAcceptFiles(Handle, False);
1077
  DragAcceptFiles(Handle, False);
1073
  TFastPHPConfig.FontSize := SynEdit1.Font.Size;
1078
  TFastPHPConfig.FontSize := SynEdit1.Font.Size;
1074
end;
1079
end;
1075
 
1080
 
1076
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
1081
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
1077
var
1082
var
1078
  r: integer;
1083
  r: integer;
1079
resourcestring
1084
resourcestring
1080
  SWantToSave = 'Do you want to save?';
1085
  SWantToSave = 'Do you want to save?';
1081
begin
1086
begin
1082
  if SynEdit1.Modified then
1087
  if SynEdit1.Modified then
1083
  begin
1088
  begin
1084
    if (ParamStr(1) <> '') or (FSaveAsFilename <> '') then
1089
    if (ParamStr(1) <> '') or (FSaveAsFilename <> '') then
1085
    begin
1090
    begin
1086
      r := MessageDlg(SWantToSave, mtConfirmation, mbYesNoCancel, 0);
1091
      r := MessageDlg(SWantToSave, mtConfirmation, mbYesNoCancel, 0);
1087
      if r = mrCancel then
1092
      if r = mrCancel then
1088
      begin
1093
      begin
1089
        CanClose := false;
1094
        CanClose := false;
1090
        Exit;
1095
        Exit;
1091
      end
1096
      end
1092
      else if r = mrYes then
1097
      else if r = mrYes then
1093
      begin
1098
      begin
1094
        ActionSave.Execute;
1099
        ActionSave.Execute;
1095
        CanClose := true;
1100
        CanClose := true;
1096
      end;
1101
      end;
1097
    end
1102
    end
1098
    else
1103
    else
1099
    begin
1104
    begin
1100
      ActionSave.Execute;
1105
      ActionSave.Execute;
1101
      CanClose := true;
1106
      CanClose := true;
1102
    end;
1107
    end;
1103
  end;
1108
  end;
1104
end;
1109
end;
1105
 
1110
 
1106
procedure TForm1.FormCreate(Sender: TObject);
1111
procedure TForm1.FormCreate(Sender: TObject);
1107
var
1112
var
1108
  exeDir: string;
1113
  exeDir: string;
1109
  sScrapFile: string;
1114
  sScrapFile: string;
1110
begin
1115
begin
1111
  HlpPrevPageIndex := -1;
1116
  HlpPrevPageIndex := -1;
1112
  CurSearchTerm := '';
1117
  CurSearchTerm := '';
1113
  sScrapFile := GetScrapFile;
1118
  sScrapFile := GetScrapFile;
1114
  Caption := Caption + ' - ' + sScrapFile;
1119
  Caption := Caption + ' - ' + sScrapFile;
1115
  Application.Title := Format('%s - FastPHP', [ExtractFileName(sScrapFile)]); // do not translate!
1120
  Application.Title := Format('%s - FastPHP', [ExtractFileName(sScrapFile)]); // do not translate!
1116
  SrcRep := TSynEditFindReplace.Create(self);
1121
  SrcRep := TSynEditFindReplace.Create(self);
1117
  SrcRep.Editor := SynEdit1;
1122
  SrcRep.Editor := SynEdit1;
1118
  SynEdit1.Gutter.Gradient := HighColorWindows;
1123
  SynEdit1.Gutter.Gradient := HighColorWindows;
1119
 
1124
 
1120
  Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
1125
  Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
1121
  SynEdit1.Gutter.Cursor := crMouseGutter;
1126
  SynEdit1.Gutter.Cursor := crMouseGutter;
1122
 
1127
 
1123
  exeDir := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
1128
  exeDir := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
1124
  if FileExists(exeDir + 'codeexplorer.bmp') then ImageList1.LoadAndSplitImages(exeDir + 'codeexplorer.bmp');
1129
  if FileExists(exeDir + 'codeexplorer.bmp') then ImageList1.LoadAndSplitImages(exeDir + 'codeexplorer.bmp');
1125
 
1130
 
1126
  FileModLast := FileAge(sScrapFile);
1131
  FileModLast := FileAge(sScrapFile);
1127
  FileModTimer.Enabled := True;
1132
  FileModTimer.Enabled := True;
1128
end;
1133
end;
1129
 
1134
 
1130
procedure TForm1.FormDestroy(Sender: TObject);
1135
procedure TForm1.FormDestroy(Sender: TObject);
1131
begin
1136
begin
1132
  if Assigned(ChmIndex) then
1137
  if Assigned(ChmIndex) then
1133
  begin
1138
  begin
1134
    FreeAndNil(ChmIndex);
1139
    FreeAndNil(ChmIndex);
1135
  end;
1140
  end;
1136
  FreeAndNil(SrcRep);
1141
  FreeAndNil(SrcRep);
1137
 
1142
 
1138
  if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
1143
  if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
1139
 
1144
 
1140
  if Assigned(codeExplorer) then
1145
  if Assigned(codeExplorer) then
1141
  begin
1146
  begin
1142
    codeExplorer.Terminate;
1147
    codeExplorer.Terminate;
1143
    codeExplorer.WaitFor;
1148
    codeExplorer.WaitFor;
1144
    FreeAndNil(codeExplorer);
1149
    FreeAndNil(codeExplorer);
1145
  end;
1150
  end;
1146
end;
1151
end;
1147
 
1152
 
1148
procedure TForm1.FormShow(Sender: TObject);
1153
procedure TForm1.FormShow(Sender: TObject);
1149
var
1154
var
1150
  ScrapFile: string;
1155
  ScrapFile: string;
1151
  tmpFontSize: integer;
1156
  tmpFontSize: integer;
1152
  opts: TSynEditorOptions;
1157
  opts: TSynEditorOptions;
1153
resourcestring
1158
resourcestring
1154
  SFileAlreadyOpen = 'File "%s" is alrady open!';
1159
  SFileAlreadyOpen = 'File "%s" is alrady open!';
1155
begin
1160
begin
1156
  if FormShowRanOnce then exit; // If the theme is changed from normal to dark, OnShow will be called another time
1161
  if FormShowRanOnce then exit; // If the theme is changed from normal to dark, OnShow will be called another time
1157
  FormShowRanOnce := true;
1162
  FormShowRanOnce := true;
1158
 
1163
 
1159
  DoubleBuffered := true;
1164
  DoubleBuffered := true;
1160
 
1165
 
1161
  ScrapFile := GetScrapFile;
1166
  ScrapFile := GetScrapFile;
1162
  if ScrapFile = '' then
1167
  if ScrapFile = '' then
1163
  begin
1168
  begin
1164
    Application.Terminate; // Close;
1169
    Application.Terminate; // Close;
1165
    exit;
1170
    exit;
1166
  end;
1171
  end;
1167
 
1172
 
1168
  opts := SynEdit1.Options;
1173
  opts := SynEdit1.Options;
1169
  if TFastPHPConfig.SpecialChars then
1174
  if TFastPHPConfig.SpecialChars then
1170
  begin
1175
  begin
1171
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1176
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1172
    Include(opts, eoShowSpecialChars);
1177
    Include(opts, eoShowSpecialChars);
1173
  end
1178
  end
1174
  else
1179
  else
1175
  begin
1180
  begin
1176
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1181
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1177
    Exclude(opts, eoShowSpecialChars);
1182
    Exclude(opts, eoShowSpecialChars);
1178
  end;
1183
  end;
1179
  SynEdit1.Options := opts;
1184
  SynEdit1.Options := opts;
1180
 
1185
 
1181
  PageControl1.ActivePage := PlaintextTabSheet;
1186
  PageControl1.ActivePage := PlaintextTabSheet;
1182
 
1187
 
1183
  PageControl2.ActivePage := CodeTabsheet;
1188
  PageControl2.ActivePage := CodeTabsheet;
1184
  HelpTabsheet.TabVisible := false;
1189
  HelpTabsheet.TabVisible := false;
1185
 
1190
 
1186
  tmpFontSize := TFastPHPConfig.FontSize;
1191
  tmpFontSize := TFastPHPConfig.FontSize;
1187
  if tmpFontSize <> -1 then SynEdit1.Font.Size := tmpFontSize;
1192
  if tmpFontSize <> -1 then SynEdit1.Font.Size := tmpFontSize;
1188
 
1193
 
1189
  if FileExists(ScrapFile) then
1194
  if FileExists(ScrapFile) then
1190
  begin
1195
  begin
1191
    if hMutex = 0 then
1196
    if hMutex = 0 then
1192
    begin
1197
    begin
1193
      hMutex := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(ScrapFile)))); // do not translate
1198
      hMutex := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(ScrapFile)))); // do not translate
1194
      if GetLastError = ERROR_ALREADY_EXISTS then
1199
      if GetLastError = ERROR_ALREADY_EXISTS then
1195
      begin
1200
      begin
1196
        // TODO: It would be great if the window of that FastPHP instance would switched to foreground
1201
        // TODO: It would be great if the window of that FastPHP instance would switched to foreground
1197
        ShowMessageFmt(SFileAlreadyOpen, [ScrapFile]);
1202
        ShowMessageFmt(SFileAlreadyOpen, [ScrapFile]);
1198
        Application.Terminate; // Close;
1203
        Application.Terminate; // Close;
1199
        exit;
1204
        exit;
1200
      end;
1205
      end;
1201
 
1206
 
1202
      SynEdit1.Lines.LoadFromFile(ScrapFile);
1207
      SynEdit1.Lines.LoadFromFile(ScrapFile);
1203
    end;
1208
    end;
1204
  end
1209
  end
1205
  else
1210
  else
1206
    SynEdit1.Lines.Clear;
1211
    SynEdit1.Lines.Clear;
1207
 
1212
 
-
 
1213
  SynEdit1.Modified := false; // is required for Delphi 11
-
 
1214
 
1208
  SynEdit1.SetFocus;
1215
  SynEdit1.SetFocus;
1209
 
1216
 
1210
  StartCodeExplorer;
1217
  StartCodeExplorer;
1211
 
1218
 
1212
  DragAcceptFiles(Handle, True);
1219
  DragAcceptFiles(Handle, True);
1213
 
1220
 
1214
  StartupTimer.Enabled := true;
1221
  StartupTimer.Enabled := true;
1215
end;
1222
end;
1216
 
1223
 
1217
function TForm1.ContainsUnicodeCharsInAnsiFile: boolean;
1224
function TForm1.ContainsUnicodeCharsInAnsiFile: boolean;
1218
var
1225
var
1219
  lines: TStringList;
1226
  lines: TStringList;
1220
  ms: TMemoryStream;
1227
  ms: TMemoryStream;
1221
begin
1228
begin
1222
  if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
1229
  if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
1223
  begin
1230
  begin
1224
    result := false;
1231
    result := false;
1225
    exit;
1232
    exit;
1226
  end;
1233
  end;
1227
 
1234
 
1228
  lines := TStringList.Create;
1235
  lines := TStringList.Create;
1229
  ms := TMemoryStream.Create;
1236
  ms := TMemoryStream.Create;
1230
  try
1237
  try
1231
    SynEdit1.Lines.SaveToStream(ms);
1238
    SynEdit1.Lines.SaveToStream(ms);
1232
    ms.Position := 0;
1239
    ms.Position := 0;
1233
    lines.LoadFromStream(ms);
1240
    lines.LoadFromStream(ms);
1234
    result := Trim(lines.Text) <> Trim(synedit1.Lines.Text);
1241
    result := Trim(lines.Text) <> Trim(synedit1.Lines.Text);
1235
  finally
1242
  finally
1236
    FreeAndNil(ms);
1243
    FreeAndNil(ms);
1237
    FreeAndNil(lines);
1244
    FreeAndNil(lines);
1238
  end;
1245
  end;
1239
end;
1246
end;
1240
 
1247
 
1241
procedure TForm1.SaveToFile(filename: string);
1248
procedure TForm1.SaveToFile(filename: string);
1242
var
1249
var
1243
  ss: TStringStream;
1250
  ss: TStringStream;
1244
  ms: TMemoryStream;
1251
  ms: TMemoryStream;
1245
  fs: TFileStream;
1252
  fs: TFileStream;
1246
  eolStyle: string;
1253
  eolStyle: string;
1247
  str: string;
1254
  str: string;
1248
  UpgradeEncodingToUnicode: boolean;
1255
  UpgradeEncodingToUnicode: boolean;
1249
  DoReloadEncoding: boolean;
1256
  DoReloadEncoding: boolean;
1250
resourcestring
1257
resourcestring
1251
  LNG_UpgradeToUtf8 = 'Unicode characters have been inserted to this ANSI encoded file. Convert file to UTF-8? (If you choose "No", the Unicode characters will be replaced with "?")';
1258
  LNG_UpgradeToUtf8 = 'Unicode characters have been inserted to this ANSI encoded file. Convert file to UTF-8? (If you choose "No", the Unicode characters will be replaced with "?")';
1252
begin
1259
begin
1253
  FileModTimer.Enabled := false;
1260
  FileModTimer.Enabled := false;
1254
 
1261
 
1255
  if ContainsUnicodeCharsInAnsiFile then
1262
  if ContainsUnicodeCharsInAnsiFile then
1256
  begin
1263
  begin
1257
    DoReloadEncoding := true;
1264
    DoReloadEncoding := true;
1258
    case MessageBox(Handle, PChar(LNG_UpgradeToUtf8), PChar(Caption), MB_YESNOCANCEL) of
1265
    case MessageBox(Handle, PChar(LNG_UpgradeToUtf8), PChar(Caption), MB_YESNOCANCEL) of
1259
      ID_YES:
1266
      ID_YES:
1260
      begin
1267
      begin
1261
        UpgradeEncodingToUnicode := true;
1268
        UpgradeEncodingToUnicode := true;
1262
      end;
1269
      end;
1263
      ID_NO:
1270
      ID_NO:
1264
      begin
1271
      begin
1265
        UpgradeEncodingToUnicode := false;
1272
        UpgradeEncodingToUnicode := false;
1266
      end;
1273
      end;
1267
      ID_CANCEL:
1274
      ID_CANCEL:
1268
      begin
1275
      begin
1269
        Abort;
1276
        Abort;
1270
      end;
1277
      end;
1271
    end;
1278
    end;
1272
  end
1279
  end
1273
  else
1280
  else
1274
  begin
1281
  begin
1275
    DoReloadEncoding := false;
1282
    DoReloadEncoding := false;
1276
    UpgradeEncodingToUnicode := false;
1283
    UpgradeEncodingToUnicode := false;
1277
  end;
1284
  end;
1278
 
1285
 
1279
  ms := TMemoryStream.Create;
1286
  ms := TMemoryStream.Create;
1280
  ss := TStringStream.Create('');
1287
  ss := TStringStream.Create('');
1281
  fs := TFileStream.Create(filename, fmCreate);
1288
  fs := TFileStream.Create(filename, fmCreate);
1282
  try
1289
  try
1283
    // Save everything in a memory stream and then to a string
1290
    // Save everything in a memory stream and then to a string
1284
    // in comparison to "str := SynEdit1.Lines.Text;",
1291
    // in comparison to "str := SynEdit1.Lines.Text;",
1285
    // This approach should preserve LF / CRLF line endings
1292
    // This approach should preserve LF / CRLF line endings
1286
    if UpgradeEncodingToUnicode then
1293
    if UpgradeEncodingToUnicode then
1287
      SynEdit1.Lines.SaveToStream(ms, TEncoding.UTF8) // upgrade an ANSI file to UTF-8 (e.g. if you include Japanese characters)
1294
      SynEdit1.Lines.SaveToStream(ms, TEncoding.UTF8) // upgrade an ANSI file to UTF-8 (e.g. if you include Japanese characters)
1288
    else
1295
    else
1289
      SynEdit1.Lines.SaveToStream(ms);
1296
      SynEdit1.Lines.SaveToStream(ms);
1290
    if DoReloadEncoding then
1297
    if DoReloadEncoding then
1291
    begin
1298
    begin
1292
      ms.Position := 0;
1299
      ms.Position := 0;
1293
      SynEdit1.Lines.LoadFromStream(ms);
1300
      SynEdit1.Lines.LoadFromStream(ms);
1294
    end;
1301
    end;
1295
    ms.Position := 0;
1302
    ms.Position := 0;
1296
    ss.CopyFrom(ms, ms.Size);
1303
    ss.CopyFrom(ms, ms.Size);
1297
    ss.Position := 0;
1304
    ss.Position := 0;
1298
    str := ss.ReadString(ss.Size);
1305
    str := ss.ReadString(ss.Size);
1299
    ss.Size := 0; // clear string-stream, because we need it later again
1306
    ss.Size := 0; // clear string-stream, because we need it later again
1300
 
1307
 
1301
    // Detect current line-endings
1308
    // Detect current line-endings
1302
    if Copy(str, 1, 2) = '#!' then
1309
    if Copy(str, 1, 2) = '#!' then
1303
    begin
1310
    begin
1304
      // Shebang. Use ONLY Linux LF
1311
      // Shebang. Use ONLY Linux LF
1305
      str := StringReplace(str, #13#10, #10, [rfReplaceAll]);
1312
      str := StringReplace(str, #13#10, #10, [rfReplaceAll]);
1306
      eolStyle := #10 // Linux LF
1313
      eolStyle := #10 // Linux LF
1307
    end
1314
    end
1308
    else
1315
    else
1309
    begin
1316
    begin
1310
      if Pos(#13#10, str) > 0 then
1317
      if Pos(#13#10, str) > 0 then
1311
        eolStyle := #13#10 // Windows CRLF
1318
        eolStyle := #13#10 // Windows CRLF
1312
      else if Pos(#10, str) > 0 then
1319
      else if Pos(#10, str) > 0 then
1313
        eolStyle := #10 // Linux LF
1320
        eolStyle := #10 // Linux LF
1314
      else
1321
      else
1315
      begin
1322
      begin
1316
        if DefaultTextLineBreakStyle = tlbsLF then
1323
        if DefaultTextLineBreakStyle = tlbsLF then
1317
          eolStyle := #10 // Linux LF
1324
          eolStyle := #10 // Linux LF
1318
        else if DefaultTextLineBreakStyle = tlbsCRLF then
1325
        else if DefaultTextLineBreakStyle = tlbsCRLF then
1319
          eolStyle := #13#10 // Windows CRLF
1326
          eolStyle := #13#10 // Windows CRLF
1320
        //else if DefaultTextLineBreakStyle = tlbsCR then
1327
        //else if DefaultTextLineBreakStyle = tlbsCR then
1321
        //  eolStyle := #13 // Old Mac CR
1328
        //  eolStyle := #13 // Old Mac CR
1322
        else
1329
        else
1323
          eolStyle := #13#10; // (Should not happen)
1330
          eolStyle := #13#10; // (Should not happen)
1324
      end;
1331
      end;
1325
    end;
1332
    end;
1326
 
1333
 
1327
    // Unitfy line-endings
1334
    // Unitfy line-endings
1328
    str := StringReplace(str, #13, '', [rfReplaceAll]);
1335
    str := StringReplace(str, #13, '', [rfReplaceAll]);
1329
    str := StringReplace(str, #10, eolStyle, [rfReplaceAll]);
1336
    str := StringReplace(str, #10, eolStyle, [rfReplaceAll]);
1330
 
1337
 
1331
    // Replace all trailing linebreaks by a single line break
1338
    // Replace all trailing linebreaks by a single line break
1332
    // Note: Removing all line breaks is not good, since Linux's "nano" will
1339
    // Note: Removing all line breaks is not good, since Linux's "nano" will
1333
    //       re-add a linebreak at the end of the file
1340
    //       re-add a linebreak at the end of the file
1334
    str := TrimRight(str) + eolStyle;
1341
    str := TrimRight(str) + eolStyle;
1335
 
1342
 
1336
    // Old versions of Delphi/SynEdit write an UTF-8 BOM, which makes problems
1343
    // Old versions of Delphi/SynEdit write an UTF-8 BOM, which makes problems
1337
    // e.g. with AJAX handlers (because AJAX reponses must not have a BOM).
1344
    // e.g. with AJAX handlers (because AJAX reponses must not have a BOM).
1338
    // So we try to avoid that.
1345
    // So we try to avoid that.
1339
    // Note that the output is still UTF-8 encoded if the input file was UTF-8 encoded
1346
    // Note that the output is still UTF-8 encoded if the input file was UTF-8 encoded
1340
    if Copy(str,1,3) = #$EF#$BB#$BF then Delete(str, 1, 3);
1347
    if Copy(str,1,3) = #$EF#$BB#$BF then Delete(str, 1, 3);
1341
 
1348
 
1342
    // Now save to the file
1349
    // Now save to the file
1343
    ss.WriteString(str);
1350
    ss.WriteString(str);
1344
    ss.Position := 0;
1351
    ss.Position := 0;
1345
    fs.CopyFrom(ss, ss.Size-ss.Position);
1352
    fs.CopyFrom(ss, ss.Size-ss.Position);
1346
  finally
1353
  finally
1347
    FreeAndNil(ms);
1354
    FreeAndNil(ms);
1348
    FreeAndNil(ss);
1355
    FreeAndNil(ss);
1349
    FreeAndNil(fs);
1356
    FreeAndNil(fs);
1350
  end;
1357
  end;
1351
 
1358
 
1352
  FileModLast := FileAge(GetScrapFile);
1359
  FileModLast := FileAge(GetScrapFile);
1353
  FileModTimer.Enabled := True;
1360
  FileModTimer.Enabled := True;
1354
end;
1361
end;
1355
 
1362
 
1356
procedure TForm1.StartCodeExplorer;
1363
procedure TForm1.StartCodeExplorer;
1357
begin
1364
begin
1358
  codeExplorer := TRunCodeExplorer.Create(true);
1365
  codeExplorer := TRunCodeExplorer.Create(true);
1359
  codeExplorer.InputRequestCallback := InputRequestCallback;
1366
  codeExplorer.InputRequestCallback := InputRequestCallback;
1360
  codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
1367
  codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
1361
  codeExplorer.PhpExe := GetPHPExe;
1368
  codeExplorer.PhpExe := GetPHPExe;
1362
  codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
1369
  codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
1363
  codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
1370
  codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
1364
  codeExplorer.Resume;
1371
  codeExplorer.Resume;
1365
end;
1372
end;
1366
 
1373
 
1367
procedure TForm1.StartUpTimerTimer(Sender: TObject);
1374
procedure TForm1.StartUpTimerTimer(Sender: TObject);
1368
begin
1375
begin
1369
  StartupTimer.Enabled := false;
1376
  StartupTimer.Enabled := false;
1370
 
1377
 
1371
  // We need this timer because we cannot change the Theme during OnShow,
1378
  // We need this timer because we cannot change the Theme during OnShow,
1372
  // because the Delphi VCL Theme is buggy!
1379
  // because the Delphi VCL Theme is buggy!
1373
 
1380
 
1374
  if TFastPHPConfig.DarkTheme then
1381
  if TFastPHPConfig.DarkTheme then
1375
  begin
1382
  begin
1376
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1383
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1377
    Theme_Dark;
1384
    Theme_Dark;
1378
  end
1385
  end
1379
  else
1386
  else
1380
  begin
1387
  begin
1381
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1388
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1382
    Theme_Light;
1389
    Theme_Light;
1383
  end;
1390
  end;
1384
end;
1391
end;
1385
 
1392
 
1386
function TForm1.GetScrapFile: string;
1393
function TForm1.GetScrapFile: string;
1387
var
1394
var
1388
  tmpPath: string;
1395
  tmpPath: string;
1389
resourcestring
1396
resourcestring
1390
  SFileDoesNotExistsCreate = 'File %s does not exist. Create it?';
1397
  SFileDoesNotExistsCreate = 'File %s does not exist. Create it?';
1391
  SPathDoesNotExistTryAgain = 'Path does not exist! Please try again.';
1398
  SPathDoesNotExistTryAgain = 'Path does not exist! Please try again.';
1392
begin
1399
begin
1393
  if FSaveAsFilename <> '' then
1400
  if FSaveAsFilename <> '' then
1394
  begin
1401
  begin
1395
    result := FSaveAsFilename;
1402
    result := FSaveAsFilename;
1396
    exit;
1403
    exit;
1397
  end;
1404
  end;
1398
 
1405
 
1399
  if FScrapFile <> '' then
1406
  if FScrapFile <> '' then
1400
  begin
1407
  begin
1401
    result := FScrapFile;
1408
    result := FScrapFile;
1402
    exit;
1409
    exit;
1403
  end;
1410
  end;
1404
 
1411
 
1405
  if ParamStr(1) <> '' then
1412
  if ParamStr(1) <> '' then
1406
  begin
1413
  begin
1407
    // Program was started with a filename
1414
    // Program was started with a filename
1408
 
1415
 
1409
    result := ParamStr(1);
1416
    result := ParamStr(1);
1410
 
1417
 
1411
    if not FileExists(result) then
1418
    if not FileExists(result) then
1412
    begin
1419
    begin
1413
      case MessageDlg(Format(SFileDoesNotExistsCreate, [result]), mtConfirmation, mbYesNoCancel, 0) of
1420
      case MessageDlg(Format(SFileDoesNotExistsCreate, [result]), mtConfirmation, mbYesNoCancel, 0) of
1414
        mrYes:
1421
        mrYes:
1415
          try
1422
          try
1416
            SaveToFile(result);
1423
            SaveToFile(result);
1417
          except
1424
          except
1418
            on E: Exception do
1425
            on E: Exception do
1419
            begin
1426
            begin
1420
              MessageDlg(E.Message, mtError, [mbOk], 0);
1427
              MessageDlg(E.Message, mtError, [mbOk], 0);
1421
              Application.Terminate;
1428
              Application.Terminate;
1422
              result := '';
1429
              result := '';
1423
              exit;
1430
              exit;
1424
            end;
1431
            end;
1425
          end;
1432
          end;
1426
        mrNo:
1433
        mrNo:
1427
          begin
1434
          begin
1428
            Application.Terminate;
1435
            Application.Terminate;
1429
            result := '';
1436
            result := '';
1430
            exit;
1437
            exit;
1431
          end;
1438
          end;
1432
        mrCancel:
1439
        mrCancel:
1433
          begin
1440
          begin
1434
            Application.Terminate;
1441
            Application.Terminate;
1435
            result := '';
1442
            result := '';
1436
            exit;
1443
            exit;
1437
          end;
1444
          end;
1438
      end;
1445
      end;
1439
    end;
1446
    end;
1440
  end
1447
  end
1441
  else
1448
  else
1442
  begin
1449
  begin
1443
    // Program is started without filename -> use scrap file
1450
    // Program is started without filename -> use scrap file
1444
 
1451
 
1445
    result := TFastPHPConfig.ScrapFile;
1452
    result := TFastPHPConfig.ScrapFile;
1446
 
1453
 
1447
    if not FileExists(result) then
1454
    if not FileExists(result) then
1448
    begin
1455
    begin
1449
      repeat
1456
      repeat
1450
        {$REGION 'Determinate opendialog initial directory'}
1457
        {$REGION 'Determinate opendialog initial directory'}
1451
        if result <> '' then
1458
        if result <> '' then
1452
        begin
1459
        begin
1453
          tmpPath := ExtractFilePath(result);
1460
          tmpPath := ExtractFilePath(result);
1454
          if DirectoryExists(tmpPath) then
1461
          if DirectoryExists(tmpPath) then
1455
          begin
1462
          begin
1456
            OpenDialog3.InitialDir := tmpPath;
1463
            OpenDialog3.InitialDir := tmpPath;
1457
            OpenDialog3.FileName := Result;
1464
            OpenDialog3.FileName := Result;
1458
          end
1465
          end
1459
          else
1466
          else
1460
          begin
1467
          begin
1461
            OpenDialog3.InitialDir := GetMyDocumentsFolder;
1468
            OpenDialog3.InitialDir := GetMyDocumentsFolder;
1462
          end;
1469
          end;
1463
        end
1470
        end
1464
        else
1471
        else
1465
        begin
1472
        begin
1466
          OpenDialog3.InitialDir := GetMyDocumentsFolder;
1473
          OpenDialog3.InitialDir := GetMyDocumentsFolder;
1467
        end;
1474
        end;
1468
        {$ENDREGION}
1475
        {$ENDREGION}
1469
 
1476
 
1470
        if not OpenDialog3.Execute then
1477
        if not OpenDialog3.Execute then
1471
        begin
1478
        begin
1472
          Application.Terminate;
1479
          Application.Terminate;
1473
          result := '';
1480
          result := '';
1474
          exit;
1481
          exit;
1475
        end;
1482
        end;
1476
 
1483
 
1477
        if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
1484
        if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
1478
        begin
1485
        begin
1479
          MessageDlg(SPathDoesNotExistTryAgain, mtWarning, [mbOk], 0);
1486
          MessageDlg(SPathDoesNotExistTryAgain, mtWarning, [mbOk], 0);
1480
        end
1487
        end
1481
        else
1488
        else
1482
        begin
1489
        begin
1483
          result := OpenDialog3.FileName;
1490
          result := OpenDialog3.FileName;
1484
        end;
1491
        end;
1485
      until result <> '';
1492
      until result <> '';
1486
 
1493
 
1487
      if not FileExists(result) then
1494
      if not FileExists(result) then
1488
      begin
1495
      begin
1489
        try
1496
        try
1490
          // Try saving the file; check if we have permissions
1497
          // Try saving the file; check if we have permissions
1491
          //SynEdit1.Lines.Clear;
1498
          //SynEdit1.Lines.Clear;
1492
          SaveToFile(result);
1499
          SaveToFile(result);
1493
        except
1500
        except
1494
          on E: Exception do
1501
          on E: Exception do
1495
          begin
1502
          begin
1496
            MessageDlg(E.Message, mtError, [mbOk], 0);
1503
            MessageDlg(E.Message, mtError, [mbOk], 0);
1497
            Application.Terminate;
1504
            Application.Terminate;
1498
            result := '';
1505
            result := '';
1499
            exit;
1506
            exit;
1500
          end;
1507
          end;
1501
        end;
1508
        end;
1502
      end;
1509
      end;
1503
 
1510
 
1504
      TFastPHPConfig.ScrapFile := result;
1511
      TFastPHPConfig.ScrapFile := result;
1505
      FScrapFile := result;
1512
      FScrapFile := result;
1506
    end;
1513
    end;
1507
  end;
1514
  end;
1508
end;
1515
end;
1509
 
1516
 
1510
procedure TForm1.Help;
1517
procedure TForm1.Help;
1511
var
1518
var
1512
  IndexFile, chmFile, w, OriginalWord, url: string;
1519
  IndexFile, chmFile, w, OriginalWord, url: string;
1513
  internalHtmlFile: string;
1520
  internalHtmlFile: string;
1514
resourcestring
1521
resourcestring
1515
  SChmFileNotAValidPHPDocumentation = 'The CHM file is not a valid PHP documentation. Cannot use help.';
1522
  SChmFileNotAValidPHPDocumentation = 'The CHM file is not a valid PHP documentation. Cannot use help.';
1516
  SUnknownErrorCannotUseHelp = 'Unknown error. Cannot use help.';
1523
  SUnknownErrorCannotUseHelp = 'Unknown error. Cannot use help.';
1517
  SNoHelpAvailable = 'No help for "%s" available';
1524
  SNoHelpAvailable = 'No help for "%s" available';
1518
begin
1525
begin
1519
  if not Assigned(ChmIndex) then
1526
  if not Assigned(ChmIndex) then
1520
  begin
1527
  begin
1521
    IndexFile := TFastPHPConfig.HelpIndex;
1528
    IndexFile := TFastPHPConfig.HelpIndex;
1522
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
1529
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
1523
    if FileExists(IndexFile) then
1530
    if FileExists(IndexFile) then
1524
    begin
1531
    begin
1525
      ChmIndex := TMemIniFile.Create(IndexFile);
1532
      ChmIndex := TMemIniFile.Create(IndexFile);
1526
    end;
1533
    end;
1527
  end;
1534
  end;
1528
 
1535
 
1529
  if Assigned(ChmIndex) then
1536
  if Assigned(ChmIndex) then
1530
  begin
1537
  begin
1531
    IndexFile := TFastPHPConfig.HelpIndex;
1538
    IndexFile := TFastPHPConfig.HelpIndex;
1532
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
1539
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
1533
 
1540
 
1534
    chmFile := ChangeFileExt(IndexFile, '.chm');
1541
    chmFile := ChangeFileExt(IndexFile, '.chm');
1535
    if not FileExists(chmFile) then
1542
    if not FileExists(chmFile) then
1536
    begin
1543
    begin
1537
      FreeAndNil(ChmIndex);
1544
      FreeAndNil(ChmIndex);
1538
    end;
1545
    end;
1539
  end;
1546
  end;
1540
 
1547
 
1541
  if not Assigned(ChmIndex) then
1548
  if not Assigned(ChmIndex) then
1542
  begin
1549
  begin
1543
    if not OpenDialog1.Execute then exit;
1550
    if not OpenDialog1.Execute then exit;
1544
 
1551
 
1545
    chmFile := OpenDialog1.FileName;
1552
    chmFile := OpenDialog1.FileName;
1546
    if not FileExists(chmFile) then exit;
1553
    if not FileExists(chmFile) then exit;
1547
 
1554
 
1548
    IndexFile := ChangeFileExt(chmFile, '.ini');
1555
    IndexFile := ChangeFileExt(chmFile, '.ini');
1549
 
1556
 
1550
    if not FileExists(IndexFile) then
1557
    if not FileExists(IndexFile) then
1551
    begin
1558
    begin
1552
      Panel1.Align := alClient;
1559
      Panel1.Align := alClient;
1553
      Panel1.Visible := true;
1560
      Panel1.Visible := true;
1554
      Panel1.BringToFront;
1561
      Panel1.BringToFront;
1555
      Screen.Cursor := crHourGlass;
1562
      Screen.Cursor := crHourGlass;
1556
      Application.ProcessMessages;
1563
      Application.ProcessMessages;
1557
      try
1564
      try
1558
        if not ParseCHM(chmFile) then
1565
        if not ParseCHM(chmFile) then
1559
        begin
1566
        begin
1560
          MessageDlg(SChmFileNotAValidPHPDocumentation, mtError, [mbOk], 0);
1567
          MessageDlg(SChmFileNotAValidPHPDocumentation, mtError, [mbOk], 0);
1561
          exit;
1568
          exit;
1562
        end;
1569
        end;
1563
      finally
1570
      finally
1564
        Screen.Cursor := crDefault;
1571
        Screen.Cursor := crDefault;
1565
        Panel1.Visible := false;
1572
        Panel1.Visible := false;
1566
      end;
1573
      end;
1567
 
1574
 
1568
      if not FileExists(IndexFile) then
1575
      if not FileExists(IndexFile) then
1569
      begin
1576
      begin
1570
        MessageDlg(SUnknownErrorCannotUseHelp, mtError, [mbOk], 0);
1577
        MessageDlg(SUnknownErrorCannotUseHelp, mtError, [mbOk], 0);
1571
        exit;
1578
        exit;
1572
      end;
1579
      end;
1573
    end;
1580
    end;
1574
 
1581
 
1575
    TFastPHPConfig.HelpIndex := IndexFile;
1582
    TFastPHPConfig.HelpIndex := IndexFile;
1576
 
1583
 
1577
    ChmIndex := TMemIniFile.Create(IndexFile);
1584
    ChmIndex := TMemIniFile.Create(IndexFile);
1578
  end;
1585
  end;
1579
 
1586
 
1580
  w := GetWordUnderCaret(SynEdit1);
1587
  w := GetWordUnderCaret(SynEdit1);
1581
  if w = '' then exit;
1588
  if w = '' then exit;
1582
  {$IFDEF UNICODE}
1589
  {$IFDEF UNICODE}
1583
  if CharInSet(w[1], ['0'..'9']) then exit;
1590
  if CharInSet(w[1], ['0'..'9']) then exit;
1584
  {$ELSE}
1591
  {$ELSE}
1585
  if w[1] in ['0'..'9'] then exit;
1592
  if w[1] in ['0'..'9'] then exit;
1586
  {$ENDIF}
1593
  {$ENDIF}
1587
 
1594
 
1588
  Originalword := w;
1595
  Originalword := w;
1589
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
1596
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
1590
  w := LowerCase(w);
1597
  w := LowerCase(w);
1591
  CurSearchTerm := w;
1598
  CurSearchTerm := w;
1592
 
1599
 
1593
  internalHtmlFile := ChmIndex.ReadString('function', CurSearchTerm, ''); // do not translate
1600
  internalHtmlFile := ChmIndex.ReadString('function', CurSearchTerm, ''); // do not translate
1594
  if internalHtmlFile = '' then
1601
  if internalHtmlFile = '' then
1595
    internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, ''); // do not translate
1602
    internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, ''); // do not translate
1596
  if internalHtmlFile = '' then
1603
  if internalHtmlFile = '' then
1597
  begin
1604
  begin
1598
    HelpTabsheet.TabVisible := false;
1605
    HelpTabsheet.TabVisible := false;
1599
    HlpPrevPageIndex := -1;
1606
    HlpPrevPageIndex := -1;
1600
    ShowMessageFmt(SNoHelpAvailable, [Originalword]);
1607
    ShowMessageFmt(SNoHelpAvailable, [Originalword]);
1601
    Exit;
1608
    Exit;
1602
  end;
1609
  end;
1603
 
1610
 
1604
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
1611
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
1605
 
1612
 
1606
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
1613
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
1607
  HelpTabsheet.TabVisible := true;
1614
  HelpTabsheet.TabVisible := true;
1608
  PageControl2.ActivePage := HelpTabsheet;
1615
  PageControl2.ActivePage := HelpTabsheet;
1609
  WebBrowser2.Navigate(url);
1616
  WebBrowser2.Navigate(url);
1610
  WebBrowser2.Wait;
1617
  WebBrowser2.Wait;
1611
end;
1618
end;
1612
 
1619
 
1613
procedure TForm1.GotoLineNo(LineNo:integer);
1620
procedure TForm1.GotoLineNo(LineNo:integer);
1614
var
1621
var
1615
  line: string;
1622
  line: string;
1616
  i: integer;
1623
  i: integer;
1617
begin
1624
begin
1618
  SynEdit1.GotoLineAndCenter(LineNo);
1625
  SynEdit1.GotoLineAndCenter(LineNo);
1619
 
1626
 
1620
  // Skip indent
1627
  // Skip indent
1621
  line := SynEdit1.Lines[SynEdit1.CaretY];
1628
  line := SynEdit1.Lines[SynEdit1.CaretY];
1622
  for i := 1 to Length(line) do
1629
  for i := 1 to Length(line) do
1623
  begin
1630
  begin
1624
    {$IFDEF UNICODE}
1631
    {$IFDEF UNICODE}
1625
    if not CharInSet(line[i], [' ', #9]) then
1632
    if not CharInSet(line[i], [' ', #9]) then
1626
    {$ELSE}
1633
    {$ELSE}
1627
    if not (line[i] in [' ', #9]) then
1634
    if not (line[i] in [' ', #9]) then
1628
    {$ENDIF}
1635
    {$ENDIF}
1629
    begin
1636
    begin
1630
      SynEdit1.CaretX := i-1;
1637
      SynEdit1.CaretX := i-1;
1631
      break;
1638
      break;
1632
    end;
1639
    end;
1633
  end;
1640
  end;
1634
 
1641
 
1635
  PageControl2.ActivePage := CodeTabsheet;
1642
  PageControl2.ActivePage := CodeTabsheet;
1636
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
1643
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
1637
end;
1644
end;
1638
 
1645
 
1639
procedure TForm1.PageControl2Changing(Sender: TObject;
1646
procedure TForm1.PageControl2Changing(Sender: TObject;
1640
  var AllowChange: Boolean);
1647
  var AllowChange: Boolean);
1641
begin
1648
begin
1642
  if PageControl2.ActivePage = HelpTabsheet then
1649
  if PageControl2.ActivePage = HelpTabsheet then
1643
    HlpPrevPageIndex := -1
1650
    HlpPrevPageIndex := -1
1644
  else
1651
  else
1645
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
1652
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
1646
 
1653
 
1647
  AllowChange := true;
1654
  AllowChange := true;
1648
end;
1655
end;
1649
 
1656
 
1650
procedure TForm1.Memo2DblClick(Sender: TObject);
1657
procedure TForm1.Memo2DblClick(Sender: TObject);
1651
var
1658
var
1652
  line: string;
1659
  line: string;
1653
 
1660
 
1654
  procedure _process(toFind: string);
1661
  procedure _process(toFind: string);
1655
  var
1662
  var
1656
    p, lineno: integer;
1663
    p, lineno: integer;
1657
  begin
1664
  begin
1658
    if FileSystemCaseSensitive then
1665
    if FileSystemCaseSensitive then
1659
      p := Pos(toFind, line)
1666
      p := Pos(toFind, line)
1660
    else
1667
    else
1661
      p := Pos(LowerCase(toFind), LowerCase(line));
1668
      p := Pos(LowerCase(toFind), LowerCase(line));
1662
    if p <> 0 then
1669
    if p <> 0 then
1663
    begin
1670
    begin
1664
      line := copy(line, p+length(toFind), 99); // TODO: "(test.php:123)" does not work, because "123)" will be tried to convert to Integer
1671
      line := copy(line, p+length(toFind), 99); // TODO: "(test.php:123)" does not work, because "123)" will be tried to convert to Integer
1665
      if not TryStrToInt(line, lineno) then exit;
1672
      if not TryStrToInt(line, lineno) then exit;
1666
      GotoLineNo(lineno);
1673
      GotoLineNo(lineno);
1667
    end;
1674
    end;
1668
  end;
1675
  end;
1669
 
1676
 
1670
begin
1677
begin
1671
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
1678
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
1672
 
1679
 
1673
  {$REGION 'Possibility 1: filename.php:lineno'}
1680
  {$REGION 'Possibility 1: filename.php:lineno'}
1674
  _process(ExtractFileName(GetScrapFile) + ':');
1681
  _process(ExtractFileName(GetScrapFile) + ':');
1675
  {$ENDREGION}
1682
  {$ENDREGION}
1676
 
1683
 
1677
  {$REGION 'Possibility 2: on line xx'}
1684
  {$REGION 'Possibility 2: on line xx'}
1678
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
1685
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
1679
  {$ENDREGION}
1686
  {$ENDREGION}
1680
end;
1687
end;
1681
 
1688
 
1682
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
1689
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
1683
  Shift: TShiftState);
1690
  Shift: TShiftState);
1684
begin
1691
begin
1685
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
1692
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
1686
end;
1693
end;
1687
 
1694
 
1688
function TForm1.MarkUpLineReference(cont: string): string;
1695
function TForm1.MarkUpLineReference(cont: string): string;
1689
 
1696
 
1690
  procedure _process(toFind: string);
1697
  procedure _process(toFind: string);
1691
  var
1698
  var
1692
    p, a, b: integer;
1699
    p, a, b: integer;
1693
    num: integer;
1700
    num: integer;
1694
    insert_a, insert_b: string;
1701
    insert_a, insert_b: string;
1695
  begin
1702
  begin
1696
    if FileSystemCaseSensitive then
1703
    if FileSystemCaseSensitive then
1697
      p := Pos(toFind, cont)
1704
      p := Pos(toFind, cont)
1698
    else
1705
    else
1699
      p := Pos(LowerCase(toFind), LowerCase(cont));
1706
      p := Pos(LowerCase(toFind), LowerCase(cont));
1700
    while p >= 1 do
1707
    while p >= 1 do
1701
    begin
1708
    begin
1702
      a := p;
1709
      a := p;
1703
      b := p + length(toFind);
1710
      b := p + length(toFind);
1704
      num := 0;
1711
      num := 0;
1705
      {$IFDEF UNICODE}
1712
      {$IFDEF UNICODE}
1706
      while CharInSet(cont[b], ['0'..'9']) do
1713
      while CharInSet(cont[b], ['0'..'9']) do
1707
      {$ELSE}
1714
      {$ELSE}
1708
      while cont[b] in ['0'..'9'] do
1715
      while cont[b] in ['0'..'9'] do
1709
      {$ENDIF}
1716
      {$ENDIF}
1710
      begin
1717
      begin
1711
        num := num*10 + StrToInt(cont[b]);
1718
        num := num*10 + StrToInt(cont[b]);
1712
        inc(b);
1719
        inc(b);
1713
      end;
1720
      end;
1714
 
1721
 
1715
      insert_b := '</a>';
1722
      insert_b := '</a>';
1716
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
1723
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
1717
 
1724
 
1718
      insert(insert_b, cont, b);
1725
      insert(insert_b, cont, b);
1719
      insert(insert_a, cont, a);
1726
      insert(insert_a, cont, a);
1720
 
1727
 
1721
      p := b + Length(insert_a) + Length(insert_b);
1728
      p := b + Length(insert_a) + Length(insert_b);
1722
 
1729
 
1723
      p := PosEx(toFind, cont, p+1);
1730
      p := PosEx(toFind, cont, p+1);
1724
    end;
1731
    end;
1725
  end;
1732
  end;
1726
 
1733
 
1727
begin
1734
begin
1728
  {$REGION 'Possibility 1: filename.php:lineno'}
1735
  {$REGION 'Possibility 1: filename.php:lineno'}
1729
  _process(ExtractFileName(GetScrapFile) + ':');
1736
  _process(ExtractFileName(GetScrapFile) + ':');
1730
  {$ENDREGION}
1737
  {$ENDREGION}
1731
 
1738
 
1732
  {$REGION 'Possibility 2: on line xx'}
1739
  {$REGION 'Possibility 2: on line xx'}
1733
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
1740
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
1734
  {$ENDREGION}
1741
  {$ENDREGION}
1735
 
1742
 
1736
  result := cont;
1743
  result := cont;
1737
end;
1744
end;
1738
 
1745
 
1739
function TForm1.InputRequestCallback(var data: AnsiString): boolean;
1746
function TForm1.InputRequestCallback(var data: AnsiString): boolean;
1740
begin
1747
begin
1741
  data := UTF8Encode(SynEdit1.Text);
1748
  data := UTF8Encode(SynEdit1.Text);
1742
  result := true;
1749
  result := true;
1743
end;
1750
end;
1744
 
1751
 
1745
function TForm1.IsThemeDark: boolean;
1752
function TForm1.IsThemeDark: boolean;
1746
begin
1753
begin
1747
  result := Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows');
1754
  result := Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows');
1748
end;
1755
end;
1749
 
1756
 
1750
function TForm1.OutputNotifyCallback(const data: AnsiString): boolean;
1757
function TForm1.OutputNotifyCallback(const data: AnsiString): boolean;
1751
begin
1758
begin
1752
  result := TreeView1.FillWithFastPHPData(string(data));
1759
  result := TreeView1.FillWithFastPHPData(string(data));
1753
end;
1760
end;
1754
 
1761
 
1755
end.
1762
end.
1756
 
1763