Skip to content

Commit 182d9c8

Browse files
committed
Revise handling of file extensions in save dialogues
This commit applies to the custom file save dialogues used when saving units or annotated source code files. The code to detect file types and to look up filter indexes depended on file extensions being different for each file type, which cannot be guaranteed. Relevant code was updated to remove this dependency. NOTE: This change was made because HTML 5 documents, when added, will use the same .html file extension and XTHML documents.
1 parent 78cf622 commit 182d9c8

File tree

4 files changed

+74
-71
lines changed

4 files changed

+74
-71
lines changed

Src/UOpenDialogHelper.pas

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,20 @@ function FilterIndexToExt(const Dlg: TOpenDialog): string;
4444
prepended '.'.
4545
}
4646

47-
function ExtToFilterIndex(const FilterStr, Ext: string;
48-
const DefValue: Integer): Integer;
49-
{Calculates index of a file extension in a "|" delimited file filter string as
50-
used in standard file dialog boxes.
51-
@param FilterStr [in] List of file types and extensions. Has format
52-
"file desc 1|ext 1|file desc 2|ext 2 etc...".
53-
@param Ext [in] Extension to be found.
54-
@param DefValue [in] Default 1 based index to use if Ext is not in
55-
FilterStr.
56-
@return 1 based index of extension in filter string or -1 if extension not
57-
in list.
58-
}
47+
/// <summary>Calculates the index of a file type description in a &quot;|&quot;
48+
/// delimited string, as used in Windows standard file dialogue boxes.
49+
/// </summary>
50+
/// <param name="FilterStr"><c>string</c> [in] List of file types and
51+
/// extensions. Must have format
52+
/// <c>file desc 1|(*.ext1)|file desc 2|(*.ext2)</c> etc...</param>
53+
/// <param name="Desc"><c>string</c> [in] File type description to be found.
54+
/// </param>
55+
/// <param name="DefIdx"><c>Integer</c> [in] Default 1 based index to use if
56+
/// <c>Desc</c> is not in <c>FilterStr</c>.</param>
57+
/// <returns><c>Integer</c>. 1 based index of the file type description in the
58+
/// filter string, or <c>DefIdx</c> if the description is not found.</returns>
59+
function FilterDescToIndex(const FilterStr, Desc: string;
60+
const DefIdx: Integer): Integer;
5961

6062
function FileOpenEditedFileNameWithExt(const Dlg: TOpenDialog): string;
6163
{Gets full path to the file that is currently entered in a file open dialog
@@ -96,47 +98,42 @@ function FilterIndexToExt(const Dlg: TOpenDialog): string;
9698
end;
9799
end;
98100

99-
function ExtToFilterIndex(const FilterStr, Ext: string;
100-
const DefValue: Integer): Integer;
101-
{Calculates index of a file extension in a "|" delimited file filter string as
102-
used in standard file dialog boxes.
103-
@param FilterStr [in] List of file types and extensions. Has format
104-
"file desc 1|ext 1|file desc 2|ext 2 etc...".
105-
@param Ext [in] Extension to be found.
106-
@param DefValue [in] Default 1 based index to use if Ext is not in
107-
FilterStr.
108-
@return 1 based index of extension in filter string or -1 if extension not
109-
in list.
110-
}
101+
function FilterDescToIndex(const FilterStr, Desc: string;
102+
const DefIdx: Integer): Integer;
111103
var
112104
FilterParts: TStringList; // stores filter split into component parts
113-
Extensions: TStringList; // list of extensions in filter string
114-
Idx: Integer; // loops thru extensions in filter string
105+
Descs: TStringList; // list of file type descriptions in filter string
106+
Idx: Integer; // loops thru Descs in filter string
107+
DescStr: string;
108+
DescEnd: Integer;
115109
begin
116-
Extensions := nil;
110+
Descs := nil;
117111
FilterParts := TStringList.Create;
118112
try
119113
// Split filter string into parts (divided by | chars):
120-
// even number indexes are descriptions and odd indexes are extensions
114+
// even number indexes are descriptions and odd indexes are Descs
121115
StrExplode(FilterStr, '|', FilterParts);
122-
// Record only extensions (every 2nd entry starting at index 1)
123-
Extensions := TStringList.Create;
124-
Idx := 1;
116+
// Record only Descs (every 2nd entry starting at index 1)
117+
Descs := TStringList.Create;
118+
Idx := 0;
125119
while Idx < FilterParts.Count do
126120
begin
127-
Extensions.Add(ExtractFileExt(FilterParts[Idx]));
121+
DescStr := FilterParts[Idx];
122+
DescEnd := StrPos('(', DescStr) - 2;
123+
DescStr := Copy(DescStr, 1, DescEnd);
124+
Descs.Add(DescStr);
128125
Inc(Idx, 2);
129126
end;
130127
// Check if required extension in list
131-
Result := Extensions.IndexOf(Ext);
128+
Result := Descs.IndexOf(Desc);
132129
if Result >= 0 then
133-
// extension in list, increment by 1 since filter indexes are 1 based
130+
// description in list, increment by 1 since filter indexes are 1 based
134131
Inc(Result)
135132
else
136-
Result := DefValue;
133+
Result := DefIdx;
137134
finally
138-
FreeAndNil(Extensions);
139-
FreeAndNil(FilterParts);
135+
Descs.Free;
136+
FilterParts.Free;
140137
end;
141138
end;
142139

Src/USaveSourceDlg.pas

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ interface
3838
/// <summary>Type of handler for event triggered by TSaveSourceDlg to get
3939
/// list of encodings supported for a file type.</summary>
4040
/// <param name="Sender">TObject [in] Object triggering event.</param>
41-
/// <param name="Ext">string [in] Extension that defines type of file being
42-
/// queried.</param>
41+
/// <param name="FilterIdx">string [in] Filter index that specifies the type
42+
/// of file being queried.</param>
4343
/// <param name="Encodings">TSourceFileEncodings [in/out] Assigned an array
4444
/// of records that specify supported encodings.</param>
45-
TEncodingQuery = procedure(Sender: TObject; const Ext: string;
45+
TEncodingQuery = procedure(Sender: TObject; const FilterIdx: Integer;
4646
var Encodings: TSourceFileEncodings) of object;
4747

4848
type
@@ -475,7 +475,7 @@ procedure TSaveSourceDlg.DoTypeChange;
475475
// handle OnEncodingQuery)
476476
SetLength(Encodings, 0);
477477
if Assigned(fOnEncodingQuery) then
478-
fOnEncodingQuery(Self, SelectedExt, Encodings);
478+
fOnEncodingQuery(Self, FilterIndex, Encodings);
479479
if Length(Encodings) = 0 then
480480
Encodings := TSourceFileEncodings.Create(
481481
TSourceFileEncoding.Create(etSysDefault, sANSIEncoding)

Src/USaveSourceMgr.pas

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ TSaveSourceMgr = class abstract(TNoPublicConstructObject)
4949
/// Provides array of encodings supported for a file extension.</summary>
5050
/// <param name="Sender">TObject [in] Reference to object that triggered
5151
/// event.</param>
52-
/// <param name="Ext">string [in] Name of extension to check.</param>
52+
/// <param name="FilterIdx">string [in] Index of file type withing dialog's
53+
/// filter string to check.</param>
5354
/// <param name="Encodings">TSourceFileEncodings [in/out] Receives array of
5455
/// supported encodings.</param>
55-
procedure EncodingQueryHandler(Sender: TObject; const Ext: string;
56+
procedure EncodingQueryHandler(Sender: TObject; const FilterIdx: Integer;
5657
var Encodings: TSourceFileEncodings);
5758
/// <summary>Handles custom save dialog's OnPreview event. Displays source
5859
/// code appropriately formatted in preview dialog box.</summary>
@@ -81,6 +82,12 @@ TSaveSourceMgr = class abstract(TNoPublicConstructObject)
8182
/// <returns>TEncodedData - Formatted source code, syntax highlighted if
8283
/// required.</returns>
8384
function GenerateOutput(const FileType: TSourceFileType): TEncodedData;
85+
/// <summary>Returns the source file type associated with the selected
86+
/// index in the save dialogue box.</summary>
87+
/// <remarks>This method assumes that the filter string entries are in the
88+
/// same order as elements of the <c>TSourceFileType</c> enumeration.
89+
/// </remarks>
90+
function FileTypeFromFilterIdx: TSourceFileType;
8491
strict protected
8592
/// <summary>Internal constructor. Initialises managed save source dialog
8693
/// box and records information about supported file types.</summary>
@@ -178,18 +185,16 @@ procedure TSaveSourceMgr.DoExecute;
178185
begin
179186
// Set up dialog box
180187
fSaveDlg.Filter := fSourceFileInfo.FilterString;
181-
fSaveDlg.FilterIndex := ExtToFilterIndex(
188+
fSaveDlg.FilterIndex := FilterDescToIndex(
182189
fSaveDlg.Filter,
183-
fSourceFileInfo.FileTypeInfo[Preferences.SourceDefaultFileType].Extension,
190+
fSourceFileInfo.FileTypeInfo[Preferences.SourceDefaultFileType].DisplayName,
184191
1
185192
);
186193
fSaveDlg.FileName := fSourceFileInfo.DefaultFileName;
187194
// Display dialog box and save file if user OKs
188195
if fSaveDlg.Execute then
189196
begin
190-
FileType := fSourceFileInfo.FileTypeFromExt(
191-
ExtractFileExt(fSaveDlg.FileName)
192-
);
197+
FileType := FileTypeFromFilterIdx;
193198
FileContent := GenerateOutput(FileType).ToString;
194199
Encoding := TEncodingHelper.GetEncoding(fSaveDlg.SelectedEncoding);
195200
try
@@ -201,14 +206,27 @@ procedure TSaveSourceMgr.DoExecute;
201206
end;
202207

203208
procedure TSaveSourceMgr.EncodingQueryHandler(Sender: TObject;
204-
const Ext: string; var Encodings: TSourceFileEncodings);
209+
const FilterIdx: Integer; var Encodings: TSourceFileEncodings);
205210
var
206211
FileType: TSourceFileType; // type of file that has given extension
207212
begin
208-
FileType := fSourceFileInfo.FileTypeFromExt(Ext);
213+
FileType := FileTypeFromFilterIdx;
209214
Encodings := fSourceFileInfo.FileTypeInfo[FileType].Encodings;
210215
end;
211216

217+
function TSaveSourceMgr.FileTypeFromFilterIdx: TSourceFileType;
218+
var
219+
FilterIdx: Integer; // dlg FilterIndex adjusted to be 0 based
220+
begin
221+
FilterIdx := fSaveDlg.FilterIndex - 1;
222+
Assert(
223+
(FilterIdx >= Ord(Low(TSourceFileType)))
224+
and (FilterIdx <= Ord(High(TSourceFileType))),
225+
ClassName + '.FileTypeFromFilterIdx: FilerIdx out of range'
226+
);
227+
Result := TSourceFileType(FilterIdx)
228+
end;
229+
212230
function TSaveSourceMgr.GenerateOutput(const FileType: TSourceFileType):
213231
TEncodedData;
214232
var
@@ -231,7 +249,7 @@ function TSaveSourceMgr.GenerateOutput(const FileType: TSourceFileType):
231249
procedure TSaveSourceMgr.HiliteQueryHandler(Sender: TObject; const Ext: string;
232250
var CanHilite: Boolean);
233251
begin
234-
CanHilite := IsHilitingSupported(fSourceFileInfo.FileTypeFromExt(Ext));
252+
CanHilite := IsHilitingSupported(FileTypeFromFilterIdx);
235253
end;
236254

237255
constructor TSaveSourceMgr.InternalCreate;
@@ -262,6 +280,13 @@ constructor TSaveSourceMgr.InternalCreate;
262280
TSourceFileEncoding.Create(etUTF8, sUTF8Encoding)
263281
]
264282
);
283+
fSourceFileInfo.FileTypeInfo[sfHTML5] := TSourceFileTypeInfo.Create(
284+
'.html',
285+
GetFileTypeDesc(sfHTML5),
286+
[
287+
TSourceFileEncoding.Create(etUTF8, sUTF8Encoding)
288+
]
289+
);
265290
fSourceFileInfo.FileTypeInfo[sfXHTML] := TSourceFileTypeInfo.Create(
266291
'.html',
267292
GetFileTypeDesc(sfXHTML),
@@ -305,7 +330,7 @@ procedure TSaveSourceMgr.PreviewHandler(Sender: TObject);
305330
var
306331
FileType: TSourceFileType; // type of source file to preview
307332
begin
308-
FileType := fSourceFileInfo.FileTypeFromExt(fSaveDlg.SelectedExt);
333+
FileType := FileTypeFromFilterIdx;
309334
// Display preview dialog box. We use save dialog as owner to ensure preview
310335
// dialog box is aligned over save dialog box
311336
TPreviewDlg.Execute(

Src/USourceFileInfo.pas

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ TSourceFileInfo = class(TObject)
105105
/// <summary>Builds filter string for use in open / save dialog boxes from
106106
/// descriptions and file extensions of each supported file type.</summary>
107107
function FilterString: string;
108-
/// <summary>Finds source file type associated with a file extension.
109-
/// </summary>
110-
function FileTypeFromExt(const Ext: string): TSourceFileType;
111108
/// <summary>Array of information about each supported file type that is
112109
/// of use to save source dialog boxes.</summary>
113110
property FileTypeInfo[const FileType: TSourceFileType]: TSourceFileTypeInfo
@@ -132,22 +129,6 @@ implementation
132129

133130
{ TSourceFileInfo }
134131

135-
function TSourceFileInfo.FileTypeFromExt(const Ext: string): TSourceFileType;
136-
var
137-
FT: TSourceFileType; // loops thru all source file types
138-
begin
139-
// Assume text file type if extension not recognised
140-
Result := sfText;
141-
for FT := Low(TSourceFileType) to High(TSourceFileType) do
142-
begin
143-
if StrSameText(Ext, fFileTypeInfo[FT].Extension) then
144-
begin
145-
Result := FT;
146-
Break;
147-
end;
148-
end;
149-
end;
150-
151132
function TSourceFileInfo.FilterString: string;
152133
const
153134
cFilterFmt = '%0:s (*%1:s)|*%1:s'; // format string for creating file filter

0 commit comments

Comments
 (0)