Skip to content

Commit f124cd4

Browse files
authored
Merge pull request #495 from Bamboy/filedialog
FileDialog Improvements: Crash Fix, File Filtering, Permission Checking
2 parents be6993d + 964f593 commit f124cd4

File tree

4 files changed

+181
-58
lines changed

4 files changed

+181
-58
lines changed

src/Myra/Graphics2D/UI/File/FileDialog.PlatformDependent.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,8 @@ public static void AppendUserPlacesOnSystem(List<Location> appendResult, IReadOn
9696

9797
foreach (string path in places)
9898
{
99-
if (!Directory.Exists(path))
99+
if (!TryGetFileAttributes(path, out _))
100100
continue;
101-
102-
//TODO check permissions for those places here
103-
//Location.TryAccess(path);
104101

105102
appendResult.Add(new Location(string.Empty, Path.GetFileName(path), path, false ));
106103
}
@@ -191,13 +188,22 @@ private static void _GetLinuxDrives(List<Location> appendResult)
191188
if(splits[0] != "part") //TYPE
192189
continue; // We only want partitioned file systems.
193190

194-
splits[2] = splits[2].Replace(RawSpace, " ");
195-
if(string.Equals(splits[2], "System Reserved")) //LABEL
191+
splits[2] = splits[2].Replace(RawSpace, " "); //LABEL
192+
if(string.Equals(splits[2], "System Reserved"))
196193
continue;
194+
if (string.IsNullOrEmpty(splits[2]))
195+
{
196+
//Is this the main system partition?
197+
if (string.Equals(splits[3], "/home") || string.Equals(splits[3], "/"))
198+
{
199+
appendResult.Add(new Location(splits[1], "Linux System", "/", true));
200+
continue;
201+
}
202+
}
197203

198204
if(string.IsNullOrEmpty(splits[3]) || string.Equals(splits[3], "/boot"))
199205
continue;
200-
splits[3] = splits[3].Replace(RawSpace, " ");; //MOUNTPOINT
206+
splits[3] = splits[3].Replace(RawSpace, " "); //MOUNTPOINT
201207

202208
appendResult.Add(new Location(splits[1], splits[2], splits[3], true));
203209
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
namespace Myra.Graphics2D.UI.File
6+
{
7+
// === Static file IO utility methods
8+
public partial class FileDialog
9+
{
10+
/// <summary>
11+
/// Attempt to retrieve folders from the given directory <paramref name="path"/>. Result <paramref name="folders"/> is null if return value is false.
12+
/// </summary>
13+
protected static bool TryEnumerateDirectoryFolders(string path, out IEnumerable<string> folders, out string exceptionMsg)
14+
{
15+
List<string> result = new List<string>(8);
16+
try
17+
{
18+
result.AddRange(Directory.EnumerateDirectories(path));
19+
}
20+
catch (Exception e)
21+
{
22+
exceptionMsg = e.Message;
23+
folders = null;
24+
return false;
25+
}
26+
result.Sort();
27+
folders = result;
28+
exceptionMsg = null;
29+
return true;
30+
}
31+
32+
/// <summary>
33+
/// Attempt to retrieve files from the given directory <paramref name="path"/>. Result <paramref name="files"/> is null if return value is false.
34+
/// <paramref name="searchPattern"/> can be null for a wildcard.
35+
/// </summary>
36+
protected static bool TryEnumerateDirectoryFiles(string path, string searchPattern, out IEnumerable<string> files, out string exceptionMsg)
37+
{
38+
List<string> result = new List<string>(8);
39+
try
40+
{
41+
if (string.IsNullOrEmpty(searchPattern))
42+
result.AddRange(Directory.EnumerateFiles(path));
43+
else
44+
{
45+
foreach (string pattern in searchPattern.Split(new char[]{ '|' }, StringSplitOptions.RemoveEmptyEntries))
46+
result.AddRange(Directory.EnumerateFiles(path, pattern));
47+
}
48+
}
49+
catch (Exception e)
50+
{
51+
exceptionMsg = e.Message;
52+
files = null;
53+
return false;
54+
}
55+
result.Sort();
56+
files = result;
57+
exceptionMsg = null;
58+
return true;
59+
}
60+
61+
/// <summary>
62+
/// Attempt to retreive permissions about a file or directory. Return value is indicative of success and a basic attribute filter.
63+
/// </summary>
64+
protected static bool TryGetFileAttributes(string path, out FileAttributes attributes)
65+
{
66+
try
67+
{
68+
attributes = new FileInfo(path).Attributes;
69+
}
70+
catch
71+
{
72+
attributes = 0;
73+
return false;
74+
}
75+
76+
bool discard =
77+
attributes.HasFlag(FileAttributes.System) |
78+
attributes.HasFlag(FileAttributes.Offline);
79+
80+
return !discard;
81+
}
82+
}
83+
}

src/Myra/Graphics2D/UI/File/FileDialog.cs

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ public Location(string volume, string label, string path, bool isDrive)
3636
public readonly string Label;
3737
public readonly string Path;
3838
public readonly bool IsDrive;
39-
40-
public bool TryAccess() => TryAccess(this.Path);
41-
public static bool TryAccess(string path)
42-
{
43-
throw new NotImplementedException();
44-
}
4539
}
4640

4741
private const int ImageTextSpacing = 4;
@@ -117,7 +111,8 @@ public string FilePath
117111
}
118112

119113
public bool AutoAddFilterExtension { get; set; }
120-
114+
public bool ShowHiddenFiles { get; set; }
115+
121116
public IImage IconFolder { get; set; }
122117
public IImage IconDrive { get; set; }
123118

@@ -182,6 +177,9 @@ public FileDialog(FileDialogMode mode) : base(null)
182177
SetStyle(Stylesheet.DefaultStyleName);
183178
}
184179

180+
/// <summary>
181+
/// Create the navigation menu of places we can visit.
182+
/// </summary>
185183
protected virtual void PopulatePlacesListUI(ListView listView)
186184
{
187185
List<Location> placeList = new List<Location>(8);
@@ -201,6 +199,9 @@ protected virtual void PopulatePlacesListUI(ListView listView)
201199
listView.Widgets.Add( CreateListItem(placeList[index]) );
202200
}
203201

202+
/// <summary>
203+
/// Create a display widget for the given location
204+
/// </summary>
204205
protected virtual Widget CreateListItem(Location location)
205206
{
206207
var item = new HorizontalStackPanel
@@ -217,6 +218,44 @@ protected virtual Widget CreateListItem(Location location)
217218
item.Widgets.Add(new Label { Text = label });
218219
return item;
219220
}
221+
222+
/// <summary>
223+
/// Return true if <paramref name="path"/> is a valid directory, and we have permissions to access it.
224+
/// </summary>
225+
protected bool TryAccessDirectory(string path)
226+
{
227+
if (!Directory.Exists(path))
228+
return false;
229+
if (!TryGetFileAttributes(path, out FileAttributes att))
230+
return false;
231+
return FileAttributeFilter(att, _mode, ShowHiddenFiles);
232+
}
233+
234+
protected virtual bool FileAttributeFilter(FileAttributes attributes, FileDialogMode mode, bool showHidden)
235+
{
236+
bool discard =
237+
attributes.HasFlag(FileAttributes.System) |
238+
attributes.HasFlag(FileAttributes.Offline) |
239+
(attributes.HasFlag(FileAttributes.Hidden) & !showHidden);
240+
241+
switch (mode)
242+
{
243+
case FileDialogMode.SaveFile:
244+
discard |= attributes.HasFlag(FileAttributes.ReadOnly);
245+
break;
246+
case FileDialogMode.ChooseFolder:
247+
discard |= !attributes.HasFlag(FileAttributes.Directory);
248+
break;
249+
default:
250+
break;
251+
}
252+
return !discard;
253+
}
254+
255+
protected void ShowIOError(string path, string exceptionMsg)
256+
{
257+
CreateMessageBox("I/O Error", exceptionMsg);
258+
}
220259

221260
private void UpdateEnabled()
222261
{
@@ -279,7 +318,7 @@ private void OnButtonForward(object sender, EventArgs args)
279318

280319
private void SetFolder(string value, bool storeInHistory)
281320
{
282-
if (!Directory.Exists(value))
321+
if (!TryAccessDirectory(value))
283322
{
284323
return;
285324
}
@@ -362,23 +401,28 @@ private void UpdateFolder()
362401
_paths.Clear();
363402

364403
_scrollPane.ScrollPosition = Mathematics.PointZero;
365-
404+
366405
var path = _textFieldPath.Text;
367-
var folders = Directory.EnumerateDirectories(path);
368-
406+
407+
// Enumerate folders in directory
408+
bool success = TryEnumerateDirectoryFolders(path, out IEnumerable<string> collection, out string exceptionMsg);
409+
if (!success)
410+
{
411+
ShowIOError(path, exceptionMsg);
412+
return;
413+
}
414+
369415
var gridY = 0;
370-
foreach (var f in folders)
416+
foreach (string folder in collection)
371417
{
372-
var fileInfo = new FileInfo(f);
373-
if (fileInfo.Attributes.HasFlag(FileAttributes.Hidden))
374-
{
418+
success = TryGetFileAttributes(folder, out FileAttributes att);
419+
if (success)
420+
success &= FileAttributeFilter(att, _mode, ShowHiddenFiles);
421+
if (!success)
375422
continue;
376-
}
377-
378-
var prop = new Proportion();
379-
380-
_gridFiles.RowsProportions.Add(prop);
381-
423+
424+
_gridFiles.RowsProportions.Add(new Proportion());
425+
382426
var image = new Image
383427
{
384428
Renderable = IconFolder,
@@ -391,14 +435,14 @@ private void UpdateFolder()
391435

392436
var name = new Label
393437
{
394-
Text = Path.GetFileName(f),
438+
Text = Path.GetFileName(folder),
395439
};
396440
Grid.SetColumn(name, 1);
397441
Grid.SetRow(name, gridY);
398442

399443
_gridFiles.Widgets.Add(name);
400444

401-
_paths.Add(f);
445+
_paths.Add(folder);
402446

403447
++gridY;
404448
}
@@ -408,47 +452,34 @@ private void UpdateFolder()
408452
return;
409453
}
410454

411-
IEnumerable<string> files;
412-
413-
if (string.IsNullOrEmpty(Filter))
455+
// Enumerate files in directory
456+
success = TryEnumerateDirectoryFiles(path, Filter, out collection, out exceptionMsg);
457+
if (!success)
414458
{
415-
files = Directory.EnumerateFiles(path);
416-
}
417-
else
418-
{
419-
var parts = Filter.Split('|');
420-
var result = new List<string>();
421-
422-
foreach (var part in parts)
423-
{
424-
result.AddRange(Directory.EnumerateFiles(path, part));
425-
}
426-
427-
files = result;
459+
ShowIOError(path, exceptionMsg);
460+
return;
428461
}
429462

430-
foreach (var f in files)
463+
foreach (string file in collection)
431464
{
432-
var fileInfo = new FileInfo(f);
433-
if (fileInfo.Attributes.HasFlag(FileAttributes.Hidden))
434-
{
465+
success = TryGetFileAttributes(file, out FileAttributes att);
466+
if (success)
467+
success &= FileAttributeFilter(att, _mode, ShowHiddenFiles);
468+
if (!success)
435469
continue;
436-
}
437-
438-
var prop = new Proportion();
439-
440-
_gridFiles.RowsProportions.Add(prop);
470+
471+
_gridFiles.RowsProportions.Add(new Proportion());
441472

442473
var name = new Label
443474
{
444-
Text = Path.GetFileName(f),
475+
Text = Path.GetFileName(file),
445476
};
446477
Grid.SetColumn(name, 1);
447478
Grid.SetRow(name, gridY);
448479

449480
_gridFiles.Widgets.Add(name);
450481

451-
_paths.Add(f);
482+
_paths.Add(file);
452483

453484
++gridY;
454485
}
@@ -536,7 +567,7 @@ public void ApplyFileDialogStyle(FileDialogStyle style)
536567
image.Renderable = pathInfo.IsDrive ? IconDrive : IconFolder;
537568
}
538569
}
539-
570+
540571
protected override void InternalSetStyle(Stylesheet stylesheet, string name)
541572
{
542573
ApplyFileDialogStyle(stylesheet.FileDialogStyles.SafelyGetStyle(name));

src/Myra/Myra.MonoGame.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
<Compile Update="Graphics2D\UI\File\FileDialog.PlatformDependent.cs">
2121
<DependentUpon>FileDialog.cs</DependentUpon>
2222
</Compile>
23+
<Compile Update="Graphics2D\UI\File\FileDialog.Util.cs">
24+
<DependentUpon>FileDialog.cs</DependentUpon>
25+
</Compile>
2326
</ItemGroup>
2427

2528
<ItemGroup>

0 commit comments

Comments
 (0)