Skip to content

Commit a2d8d08

Browse files
committed
Make Unicode character prefix configurable
Also makes the About dialog more responsive to the dark mode setting
1 parent 7fb8043 commit a2d8d08

6 files changed

Lines changed: 246 additions & 38 deletions

File tree

src/Forms/AboutForm.pas

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ TFrmAbout = class(TfpgForm)
4545
txtFpgAuthors: TfpgPanel;
4646
lblFpgLicense: TfpgPanel;
4747
txtFpgLicense: TfpgPanel;
48-
lblHomeDir: TfpgPanel;
49-
txtHomeDir: TfpgPanel;
48+
lblUnicodeFormat: TfpgPanel;
49+
txtUnicodeConfig: TfpgPanel;
5050
lblConfigDir: TfpgPanel;
5151
txtConfigDir: TfpgPanel;
5252
lblEntities: TfpgPanel;
@@ -59,11 +59,12 @@ TFrmAbout = class(TfpgForm)
5959
constructor Create(AOwner: TComponent); override;
6060
destructor Destroy; override;
6161
procedure AfterCreate; override;
62-
published
6362
procedure DoOnShow({%H-}Sender: TObject);
63+
published
6464
procedure FormClose({%H-}Sender: TObject);
6565
procedure GoToChangelog({%H-}Sender: TObject);
6666
procedure GoToEntities({%H-}Sender: TObject);
67+
procedure ShowConfigForm({%H-}Sender: TObject);
6768
procedure FollowPath(Sender: TObject);
6869
procedure ShowLink(Sender: TObject);
6970
procedure RevertCursor(Sender: TObject);
@@ -87,10 +88,10 @@ implementation
8788

8889
uses
8990
SysUtils,
90-
StrUtils,
9191
Windows,
9292
L_SpecialFolders,
9393
NppPlugin,
94+
ConfigForm,
9495
U_Npp_HTMLTag;
9596

9697
constructor TFrmAbout.Create(AOwner: TComponent);
@@ -145,17 +146,19 @@ constructor TFrmAbout.Create(AOwner: TComponent);
145146
txtFpgLicense := MakeText(FpgLicense);
146147
lblSpacer2 := MakeText(' ', 8);
147148

148-
lblHomeDir := MakeText('Plugin location');
149-
txtHomeDir := MakeText(UTF8ToAnsi(UTF8Encode(ExtractFileDir(FDLLName))), 24);
150-
WrapFilePath(txtHomeDir);
151-
152149
lblConfigDir := MakeText('Config location');
153150
txtConfigDir := MakeText(UTF8ToAnsi(UTF8Encode(Npp.PluginConfigDir)), 24);
154151
WrapFilePath(txtConfigDir);
155152

156153
lblEntities := MakeText('HTML entities file');
157154
txtEntities := MakeText('', 24);
158155

156+
lblUnicodeFormat := MakeText('Unicode character format: ' + Npp.Options.UnicodePrefix + '0000');
157+
txtUnicodeConfig := MakeText('Configure', 24);
158+
SetUrl(txtUnicodeConfig);
159+
txtUnicodeConfig.Onclick := ShowConfigForm;
160+
txtUnicodeConfig.FontDesc := FPG_DEFAULT_FONT_DESC;
161+
159162
btnSpacer := MakeText(' ', 12);
160163

161164
btnClose := CreateButton(self, 0, 0, BtnWidth, 'OK', FormClose);
@@ -226,9 +229,25 @@ procedure TFrmAbout.GoToEntities({%H-}Sender: TObject);
226229
Close;
227230
end;
228231

232+
procedure TFrmAbout.ShowConfigForm({%H-}Sender: TObject);
233+
var
234+
Frm : TFrmConfig;
235+
Current: ShortString;
236+
begin
237+
Current := Npp.Options.UnicodePrefix;
238+
try
239+
fpgApplication.CreateForm(TFrmConfig, Frm);
240+
if (Frm.ShowModal = mrOK) then
241+
lblUnicodeFormat.Text := LeftStr(lblUnicodeFormat.Text, 25) +
242+
StringReplace(Copy(lblUnicodeFormat.Text, 26, MaxInt), Current, Npp.Options.UnicodePrefix, []);
243+
finally
244+
FreeAndNil(Frm);
245+
end;
246+
end;
247+
229248
procedure TFrmAbout.FollowPath(Sender: TObject);
230249
begin
231-
Npp.ShellExecute(PChar(ReplaceStr(TfpgPanel(Sender).Hint, NewLine, '')));
250+
Npp.ShellExecute(PChar(StringReplace(TfpgPanel(Sender).Hint, NewLine, '', [])));
232251
Close;
233252
end;
234253

@@ -249,7 +268,7 @@ procedure TFrmAbout.RevertCursor(Sender: TObject);
249268
TfpgPanel(Sender).TextColor := fpgColor($0, $BF, $FF)
250269
else
251270
TfpgPanel(Sender).TextColor := clHyperLink;
252-
TfpgPanel(Sender).FontDesc := ReplaceStr(TfpgPanel(Sender).FontDesc, ':Underline', '');
271+
TfpgPanel(Sender).FontDesc := StringReplace(TfpgPanel(Sender).FontDesc, ':Underline', '', []);
253272
end;
254273

255274
function TFrmAbout.MakeText(const Txt: string; const Height: TfpgCoord): TfpgPanel;
@@ -288,7 +307,7 @@ procedure TFrmAbout.SetConfigFilePath(Path: TfpgPanel);
288307
else
289308
begin
290309
Text := 'Not found';
291-
FontDesc := ReplaceStr(FontDesc, ':Underline', '');
310+
FontDesc := StringReplace(FontDesc, ':Underline', '', []);
292311
if Npp.DarkModeEnabled then
293312
TextColor := fpgColor($FF, $63, $47)
294313
else
@@ -333,7 +352,7 @@ procedure TFrmAbout.WrapFilePath(Path: TfpgPanel);
333352
end
334353
else
335354
begin
336-
WrapAt := Round(BtnWidth * 0.8);
355+
WrapAt := (BtnWidth div 10)*9;
337356
if (OS > WV_WIN10) then
338357
LineSpc := 8
339358
else
@@ -374,8 +393,8 @@ procedure TFrmAbout.SetDefaultStyles;
374393
lblFpgLicense.TextColor := clBlack;
375394
txtFpgAuthors.TextColor := clBlack;
376395
txtFpgLicense.TextColor := clBlack;
377-
lblHomeDir.TextColor := clBlack;
378-
txtHomeDir.TextColor := clBlack;
396+
lblUnicodeFormat.TextColor := clBlack;
397+
txtUnicodeConfig.TextColor := clHyperLink;
379398
lblConfigDir.TextColor := clBlack;
380399
txtConfigDir.TextColor := clBlack;
381400
lblEntities.TextColor := clBlack;
@@ -399,8 +418,8 @@ procedure TFrmAbout.SetDarkStyles;
399418
lblFpgLicense.TextColor := clWhite;
400419
txtFpgAuthors.TextColor := clWhite;
401420
txtFpgLicense.TextColor := clWhite;
402-
lblHomeDir.TextColor := clWhite;
403-
txtHomeDir.TextColor := clWhite;
421+
lblUnicodeFormat.TextColor := clWhite;
422+
txtUnicodeConfig.TextColor := fpgColor($0, $BF, $FF);
404423
lblConfigDir.TextColor := clWhite;
405424
txtConfigDir.TextColor := clWhite;
406425
lblEntities.TextColor := clWhite;

src/Forms/ConfigForm.pas

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
unit ConfigForm;
2+
3+
{$IFDEF FPC}{$mode delphi}{$ENDIF}
4+
5+
{
6+
Copyright (c) 2023 Robert Di Pardo <dipardo.r@gmail.com>
7+
8+
This Source Code Form is subject to the terms of the Mozilla Public License,
9+
v. 2.0. If a copy of the MPL was not distributed with this file, You can
10+
obtain one at http://mozilla.org/MPL/2.0/.
11+
}
12+
13+
interface
14+
15+
uses
16+
Classes,
17+
fpg_base,
18+
fpg_widget,
19+
fpg_form,
20+
fpg_label,
21+
fpg_memo,
22+
fpg_button;
23+
24+
const
25+
FrmHeight = 112;
26+
FrmWidth = 400;
27+
BtnWidth = 85;
28+
29+
type
30+
TFrmConfig = class(TfpgForm)
31+
txtPrefix: TfpgMemo;
32+
lblFmt, lblDigits: TfpgLabel;
33+
btnOK, btnCancel, btnReset: TfpgButton;
34+
constructor Create(AOwner: TComponent); override;
35+
procedure AfterConstruction; override;
36+
procedure DoOnClose(var CloseAction: TCloseAction); override;
37+
procedure BtnOKClick({%H-}Sender: TObject);
38+
procedure BtnResetClick({%H-}Sender: TObject);
39+
procedure ValidatePrefix(Sender: TObject);
40+
procedure SetUpButton(var Btn: TfpgButton; Value: TfpgModalResult; Index: SmallInt; Icon: TfpgString);
41+
end;
42+
43+
implementation
44+
45+
uses U_Npp_HTMLTag;
46+
47+
constructor TFrmConfig.Create(AOwner: TComponent);
48+
var
49+
BtnTop: TFpgCoord;
50+
begin
51+
inherited Create(AOwner);
52+
WindowTitle := 'Change Unicode character prefix';
53+
WindowPosition := wpScreenCenter;
54+
Height := FrmHeight;
55+
Width := FrmWidth;
56+
Sizeable := False;
57+
BtnTop := (FrmHeight div 25)*17;
58+
btnOK := CreateButton(self, ((FrmWidth div 2) - ((BtnWidth div 2)*3) - 12), BtnTop, BtnWidth, 'OK', BtnOKClick);
59+
btnReset := CreateButton(self, (btnOK.Left + BtnWidth + 8), BtnTop, BtnWidth, 'Reset', BtnResetClick);
60+
btnCancel := CreateButton(self, (btnReset.Left + BtnWidth + 8), BtnTop, BtnWidth, 'Cancel', nil);
61+
lblFmt := CreateLabel(self, (FrmWidth div 4), (self.Height div 5), 'Format:', 64, 28);
62+
txtPrefix := CreateMemo(Self, (lblFmt.Left + lblFmt.Width + 2), (lblFmt.Top - 2), 68, 28);
63+
lblDigits := CreateLabel(self, (txtPrefix.Left + txtPrefix.Width + 2), lblFmt.Top, '0000', 64, 28);
64+
with txtPrefix do begin
65+
FontDesc := 'Consolas-8';
66+
MaxLength := 8;
67+
TabOrder := 0;
68+
OnPaint := ValidatePrefix;
69+
end;
70+
lblDigits.FontDesc := txtPrefix.FontDesc + ':italic';
71+
SetUpButton(btnOK, mrOk, 1, 'stdimg.ok');
72+
SetUpButton(btnCancel, mrCancel, 2, 'stdimg.quit');
73+
SetUpButton(btnReset, mrNone, 3, 'stdimg.refresh');
74+
BtnResetClick(nil);
75+
end;
76+
77+
procedure TFrmConfig.AfterConstruction;
78+
var
79+
i: Cardinal;
80+
begin
81+
inherited;
82+
if Npp.DarkModeEnabled then begin
83+
for i := 0 to Self.ComponentCount-1 do begin
84+
with TfpgWidget(Components[i]) do begin
85+
TextColor := clWhite;
86+
if InheritsFrom(TfpgLabel) then
87+
BackgroundColor := clBlack
88+
else
89+
BackgroundColor := fpgColor($3E, $3E, $40);
90+
end;
91+
end;
92+
Self.BackgroundColor := clBlack;
93+
end else begin
94+
for i := 0 to Self.ComponentCount-1 do begin
95+
with TfpgWidget(Components[i]) do begin
96+
TextColor := clBlack;
97+
if InheritsFrom(TfpgButton) then
98+
BackgroundColor := clButtonFace
99+
else
100+
BackgroundColor := clBoxColor;
101+
end;
102+
end;
103+
Self.BackgroundColor:= clBoxColor;
104+
end;
105+
end;
106+
107+
procedure TFrmConfig.DoOnClose(var CloseAction: TCloseAction);
108+
begin
109+
inherited;
110+
CloseAction := caHide;
111+
end;
112+
113+
procedure TFrmConfig.BtnOKClick({%H-}Sender: TObject);
114+
begin
115+
Npp.SetUnicodeFormatOptions(txtPrefix.Text);
116+
end;
117+
118+
procedure TFrmConfig.BtnResetClick({%H-}Sender: TObject);
119+
begin
120+
txtPrefix.Text := Npp.Options.UnicodePrefix;
121+
ValidatePrefix(txtPrefix);
122+
end;
123+
124+
procedure TFrmConfig.ValidatePrefix(Sender: TObject);
125+
var
126+
Input : TfpgString;
127+
begin
128+
Input := TfpgMemo(Sender).Text;
129+
btnOK.Enabled := (Length(Input) > 0) and (Ord(Input[1]) > $20);
130+
end;
131+
132+
procedure TFrmConfig.SetUpButton(var Btn: TfpgButton; Value: TfpgModalResult; Index: SmallInt; Icon: TfpgString);
133+
begin
134+
with Btn do begin
135+
ModalResult := Value;
136+
TabOrder := Index;
137+
ImageName := Icon;
138+
ShowImage := (Icon <> '');
139+
end;
140+
end;
141+
142+
end.

src/LibNppPlugin/nppplugin.pas

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ TNppPlugin = class(TObject)
8383
procedure DoNppnShutdown; virtual;
8484
procedure DoNppnBufferActivated(const BufferID: THandle); virtual;
8585
procedure DoNppnFileClosed(const BufferID: THandle); virtual;
86+
procedure DoNppnThemeChanged; virtual; abstract;
8687
procedure DoAutoCSelection(const hwnd: HWND; const StartPos: Sci_Position; ListItem: nppPChar); virtual; abstract;
8788
procedure DoCharAdded(const hwnd: HWND; const ch: Integer); virtual; abstract;
8889
procedure DoUpdateUI(const hwnd: HWND; const updated: Integer); virtual;
@@ -203,6 +204,9 @@ procedure TNppPlugin.BeNotified(sn: PSCNotification);
203204
NPPN_TB_MODIFICATION: begin
204205
self.DoNppnToolbarModification;
205206
end;
207+
NPPN_DARKMODECHANGED: begin
208+
self.DoNppnThemeChanged;
209+
end;
206210
NPPN_SHUTDOWN: begin
207211
self.DoNppnShutdown;
208212
end;

src/U_JSEncode.pas

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function DecodeJS(Scope: TEntityReplacementScope = ersSelection): Integer;
2525
implementation
2626
uses
2727
SysUtils,
28+
U_Npp_HTMLTag,
2829
NppPlugin;
2930

3031
{ ------------------------------------------------------------------------------------------------ }
@@ -83,7 +84,7 @@ function DoEncodeJS(var Text: WideString): Integer; overload;
8384
CharCode := Ord(Text[CharIndex]);
8485
if CharCode > 127 then begin
8586
Text := Copy(Text, 1, CharIndex - 1)
86-
+ WideFormat('\u%s', [IntToHex(CharCode, 4)])
87+
+ WideFormat('%s%s', [U_Npp_HTMLTag.Npp.Options.UnicodePrefix, IntToHex(CharCode, 4)])
8788
+ Copy(Text, CharIndex + 1);
8889
Inc(EntitiesReplaced);
8990
end;
@@ -116,8 +117,10 @@ function DecodeJS(Scope: TEntityReplacementScope = ersSelection): Integer;
116117
npp: TApplication;
117118
doc: TActiveDocument;
118119
Target, Match, MatchNext: TTextRange;
120+
Pattern: WideString;
119121
ColumnSel: Boolean;
120-
HiByte, LoByte: Cardinal;
122+
LenPrefix: Sci_Position;
123+
HiByte, LoByte: Integer;
121124
EmojiChars: array [0..1] of WideChar;
122125
begin
123126
Result := 0;
@@ -127,25 +130,24 @@ function DecodeJS(Scope: TEntityReplacementScope = ersSelection): Integer;
127130
ColumnSel := (doc.SendMessage(SCI_GETSELECTIONMODE) <> SC_SEL_STREAM);
128131
Target := TTextRange.Create(doc, doc.Selection.StartPos, doc.Selection.EndPos);
129132
Match := TTextRange.Create(doc);
133+
Pattern := UTF8Decode(U_Npp_HTMLTag.Npp.Options.UnicodeRE);
134+
LenPrefix := Length(U_Npp_HTMLTag.Npp.Options.UnicodePrefix);
130135
try
131136
repeat
132-
doc.Find('\\u[0-9A-F]{4}', Match, SCFIND_REGEXP, Target.StartPos, Target.EndPos);
137+
doc.Find(Pattern, Match, SCFIND_REGEXP, Target.StartPos, Target.EndPos);
133138
if Match.Length <> 0 then begin
134139
// Adjust the target already
135140
Target.StartPos := Match.StartPos + 1;
136141

137-
// replace this match's text by the appropriate Unicode character
138-
HiByte := StrToInt(Format('$%s', [Copy(Match.Text, 3, 4)]));
139-
140142
// check if code point belongs to a multi-byte glyph
141-
if (HiByte >= $D800) and (HiByte <= $DBFF) then
143+
if TryStrToInt(Format('$%s', [Copy(Match.Text, lenPrefix+1, 4)]), HiByte) and
144+
(HiByte >= $D800) and (HiByte <= $DBFF) then
142145
begin
143146
try
144147
MatchNext := TTextRange.Create(doc);
145-
doc.Find('\\u[0-9A-F]{4}', MatchNext, SCFIND_REGEXP, Match.EndPos-2, Target.EndPos);
146-
if MatchNext.Length <> 0 then
148+
doc.Find(Pattern, MatchNext, SCFIND_REGEXP, Match.EndPos-lenPrefix, Target.EndPos);
149+
if (MatchNext.Length <> 0) and TryStrToInt(Format('$%s', [Copy(MatchNext.Text, lenPrefix+1, 4)]), LoByte) then
147150
begin
148-
LoByte := StrToInt(Format('$%s', [Copy(MatchNext.Text, 3, 4)]));
149151
// erase tail character
150152
MatchNext.Text := EmptyWideStr;
151153
EmojiChars[0] := WideChar(LoByte);

0 commit comments

Comments
 (0)