From 0cc50d658a4c986d4da2297d04d36a73f532d845 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 23 Jul 2020 14:13:48 +0300 Subject: [PATCH] [X11] Auto-append file extension for GTK-based file dialogs https://github.com/AvaloniaUI/Avalonia/pull/4350 --- src/Avalonia.X11/NativeDialogs/Gtk.cs | 5 ++- .../NativeDialogs/GtkNativeFileDialogs.cs | 43 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs index 9a12479fac..d19656564e 100644 --- a/src/Avalonia.X11/NativeDialogs/Gtk.cs +++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs @@ -204,7 +204,10 @@ namespace Avalonia.X11.NativeDialogs [DllImport(GtkName)] public static extern IntPtr gtk_file_chooser_add_filter(IntPtr chooser, IntPtr filter); - + + [DllImport(GtkName)] + public static extern IntPtr gtk_file_chooser_get_filter(IntPtr chooser); + [DllImport(GtkName)] public static extern void gtk_widget_realize(IntPtr gtkWidget); diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs index 5c9a0c992a..0f85147743 100644 --- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs +++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs @@ -16,7 +16,7 @@ namespace Avalonia.X11.NativeDialogs { private Task _initialized; private unsafe Task ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action, - bool multiSelect, string initialFileName, IEnumerable filters) + bool multiSelect, string initialFileName, IEnumerable filters, string defaultExtension) { IntPtr dlg; using (var name = new Utf8Buffer(title)) @@ -35,11 +35,13 @@ namespace Avalonia.X11.NativeDialogs foreach (var d in disposables) d.Dispose(); disposables.Clear(); } - + + var filtersDic = new Dictionary(); if(filters != null) foreach (var f in filters) { var filter = gtk_file_filter_new(); + filtersDic[filter] = f; using (var b = new Utf8Buffer(f.Name)) gtk_file_filter_set_name(filter, b); @@ -74,6 +76,15 @@ namespace Avalonia.X11.NativeDialogs } g_slist_free(gs); result = resultList.ToArray(); + + // GTK doesn't auto-append the extension, so we need to do that manually + if (action == GtkFileChooserAction.Save) + { + var currentFilter = gtk_file_chooser_get_filter(dlg); + filtersDic.TryGetValue(currentFilter, out var selectedFilter); + for (var c = 0; c < result.Length; c++) + result[c] = NameWithExtension(result[c], defaultExtension, selectedFilter); + } } gtk_widget_hide(dlg); @@ -101,6 +112,29 @@ namespace Avalonia.X11.NativeDialogs gtk_window_present(dlg); return tcs.Task; } + + string NameWithExtension(string path, string defaultExtension, FileDialogFilter filter) + { + var name = Path.GetFileName(path); + if (name != null && !name.Contains(".")) + { + if (filter?.Extensions?.Count > 0) + { + if (defaultExtension != null + && filter.Extensions.Contains(defaultExtension)) + return path + "." + defaultExtension.TrimStart('.'); + + var ext = filter.Extensions.FirstOrDefault(x => x != "*"); + if (ext != null) + return path + "." + ext.TrimStart('.'); + } + + if (defaultExtension != null) + path += "." + defaultExtension.TrimStart('.'); + } + + return path; + } public async Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) { @@ -110,7 +144,8 @@ namespace Avalonia.X11.NativeDialogs dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, (dialog as OpenFileDialog)?.AllowMultiple ?? false, Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory, - string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters)); + string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters, + (dialog as SaveFileDialog)?.DefaultExtension)); } public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) @@ -119,7 +154,7 @@ namespace Avalonia.X11.NativeDialogs return await await RunOnGlibThread(async () => { var res = await ShowDialog(dialog.Title, parent, - GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, null); + GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, null, null); return res?.FirstOrDefault(); }); }