Subversion Repositories fastphp

Rev

Rev 78 | Rev 80 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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