using Gtk; using Adw; using System.Text; namespace Photomator.Views; public partial class MainWindow : Adw.ApplicationWindow { private readonly Adw.Application _application; public MainWindow(Adw.Application application) : base() { _application = application; SetTitle(AppInfo.ApplicationName); SetIconName(AppInfo.IconName); PreferencesPage layoutSingle = InitPageSingle(); PreferencesPage layoutFolder = InitPageFolder(); ViewStack viewStack = ViewStack.New(); viewStack.Add(layoutSingle); ViewStackPage pageSingle = viewStack.GetPage(layoutSingle); pageSingle.SetTitle("Einzelne Datei"); pageSingle.SetIconName("image-x-generic-symbolic"); viewStack.Add(layoutFolder); ViewStackPage pageFolder = viewStack.GetPage(layoutFolder); pageFolder.SetTitle("Ordner"); pageFolder.SetIconName("folder-symbolic"); ViewSwitcher viewSwitcher = ViewSwitcher.New(); viewSwitcher.SetStack(viewStack); viewSwitcher.SetPolicy(ViewSwitcherPolicy.Wide); SetDefaultSize(600, 800); Adw.AboutDialog aboutDialog = InitAboutDialog(); Adw.HeaderBar headerBar = Adw.HeaderBar.New(); Button btnAbout = Button.NewFromIconName("help-about-symbolic"); btnAbout.OnClicked += (_, _) => aboutDialog.Present(this); headerBar.SetTitleWidget(viewSwitcher); headerBar.PackEnd(btnAbout); ToolbarView view = ToolbarView.New(); view.AddTopBar(headerBar); view.SetContent(viewStack); SetContent(view); } public void Start() { _application.AddWindow(this); Present(); } public PreferencesPage InitPageSingle() { PreferencesPage page = PreferencesPage.New(); PreferencesGroup pgSelect = PreferencesGroup.New(); pgSelect.SetTitle("Bild"); pgSelect.SetDescription("Als Eingabeformate werden BMP, GIF, JPEG, PBM, PNG, TIFF, TGA und WebP unterstützt."); EntryRow erSelect = EntryRow.New(); erSelect.SetTitle("Zu konvertierendes Bild"); erSelect.SetEditable(false); PreferencesGroup pgOptions = PreferencesGroup.New(); pgOptions.SetTitle("Optionen"); pgOptions.SetDescription("Diese Parameter beeinflussen die Qualität und Größe der Bilder."); ComboRow crRes = ComboRow.New(); crRes.SetTitle("Auflösung"); crRes.SetSubtitle("Länge der kurzen Seite in Pixel"); StringList resOptions = StringList.New(["720", "1080", "1440", "2160"]); crRes.SetModel(resOptions); crRes.SetSelected(2); pgOptions.Add(crRes); ComboRow crFormat = ComboRow.New(); crFormat.SetTitle("Konvertierungsart"); crFormat.SetModel(StringList.New(["Verlustbehaftet", "Verlustfrei"])); crFormat.SetSelected(0); pgOptions.Add(crFormat); PreferencesGroup pgBtn = PreferencesGroup.New(); pgBtn.SetValign(Align.Center); pgBtn.SetHalign(Align.Center); Button btnConvert = Button.NewWithLabel("Konvertieren"); btnConvert.OnClicked += async (_, _) => { FileDialog fileDialog = FileDialog.New(); Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType()); FileFilter filter = FileFilter.New(); filter.SetName("WebP (*.webp)"); filter.AddPattern("*.webp"); filter.AddPattern("*.WEBP"); listStore.Append(filter); fileDialog.SetFilters(listStore); Gio.File? file; try { file = await fileDialog.SaveAsync(this); } catch { file = null; } if (file != null && file.GetPath() != null) { ConvertOptions options = new(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected()); await Lib.Convert(erSelect.GetText(), file.GetPath()!, options); } else InitInfoAlert("Kein Ziel ausgewählt", "Es wurde keine Zieldatei ausgewählt. Konvertierung wird abgebrochen."); }; btnConvert.SetCssClasses(["pill", "suggested-action", "long"]); btnConvert.SetSensitive(false); pgBtn.Add(btnConvert); Button btnSelect = Button.New(); btnSelect.SetValign(Align.Center); btnSelect.AddCssClass("flat"); btnSelect.SetIconName("document-open-symbolic"); btnSelect.OnClicked += async (_, _) => { FileDialog fileDialog = FileDialog.New(); Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType()); FileFilter filterAll = FileFilter.New(); filterAll.SetName("Alle unterstützten Bildformate"); string[][] formats = [["bmp", "dib"], ["gif"], ["jpeg", "jpg", "jpe", "jfif"], ["pbm"], ["png"], ["tiff", "tif"], ["tga", "bpx", "icb", "pix"], ["webp"]]; foreach (string[] format in formats) { FileFilter filter = FileFilter.New(); StringBuilder name = new($"{format[0].ToUpper()} ("); for (int i = 0; i < format.Length; i++) { name.Append($"*.{format[i]}"); if (i != format.Length - 1) name.Append(", "); filter.AddPattern($"*.{format[i]}"); filterAll.AddPattern($"*.{format[i]}"); filter.AddPattern($"*.{format[i].ToUpper()}"); filterAll.AddPattern($"*.{format[i].ToUpper()}"); } name.Append(')'); filter.SetName(name.ToString()); listStore.Append(filter); } listStore.Insert(0, filterAll); fileDialog.SetFilters(listStore); Gio.File? file; try { file = await fileDialog.OpenAsync(this); } catch { file = null; } if (file != null && file.GetPath() != null) { erSelect.SetText(file.GetPath()!); btnConvert.SetSensitive(true); } else if (erSelect.GetText() == "") btnConvert.SetSensitive(false); }; erSelect.AddSuffix(btnSelect); pgSelect.Add(erSelect); page.Add(pgSelect); page.Add(pgOptions); page.Add(pgBtn); return page; } public PreferencesPage InitPageFolder() { EntryRow erSource = EntryRow.New(); erSource.SetTitle("Quellordner"); erSource.SetEditable(false); EntryRow erDest = EntryRow.New(); erDest.SetTitle("Zielordner"); erDest.SetEditable(false); PreferencesGroup pgOptions = PreferencesGroup.New(); pgOptions.SetTitle("Optionen"); pgOptions.SetDescription("Diese Parameter beeinflussen die Qualität und Größe der Bilder."); ComboRow crRes = ComboRow.New(); crRes.SetTitle("Auflösung"); crRes.SetSubtitle("Länge der kurzen Seite in Pixel"); StringList resOptions = StringList.New(["720", "1080", "1440", "2160"]); crRes.SetModel(resOptions); crRes.SetSelected(2); pgOptions.Add(crRes); ComboRow crFormat = ComboRow.New(); crFormat.SetTitle("Konvertierungsart"); crFormat.SetModel(StringList.New(["Verlustbehaftet", "Verlustfrei"])); crFormat.SetSelected(0); pgOptions.Add(crFormat); PreferencesGroup pgBtn = PreferencesGroup.New(); pgBtn.SetValign(Align.Center); pgBtn.SetHalign(Align.Center); Button btnConvert = Button.NewWithLabel("Konvertieren"); btnConvert.OnClicked += async (_, _) => { if (erSource.GetText() != "" && erDest.GetText() != "") { string[] filesSource; if (Directory.Exists(erSource.GetText())) filesSource = Directory.GetFiles(erSource.GetText()); else { InitInfoAlert("Quellordner existiert nicht", "Der ausgewählte Quellordner ist nicht im Dateisystem vorhanden. Die Konvertierung wurde abgebrochen."); return; } if (filesSource.Length == 0) { InitInfoAlert("Quellordner ist leer", "Im ausgewählten Quellordner befinden sich keine Dateien. Die Konvertierung wurde abgebrochen."); return; } string dest = erDest.GetText(); if (!Directory.Exists(dest)) Directory.CreateDirectory(dest); StatusDialog statusDialog = new(_application, this); statusDialog.Start(); try { ConvertOptions options = new ConvertOptions(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected()); ConvertError[] convertErrors = await Lib.ConvertList(filesSource, dest, options, statusDialog.SetProgress); if (convertErrors.Length > 0) statusDialog.Partial(filesSource.Length, dest, convertErrors); else statusDialog.Success(filesSource.Length, dest); } catch (Exception e) { statusDialog.Error(e.Message); } } }; btnConvert.SetCssClasses(["pill", "suggested-action", "long"]); btnConvert.SetSensitive(false); pgBtn.Add(btnConvert); void checkButtonState() { if (erSource.GetText() != "" && erDest.GetText() != "") btnConvert.SetSensitive(true); else btnConvert.SetSensitive(false); } Button btnSource = Button.New(); btnSource.SetValign(Align.Center); btnSource.AddCssClass("flat"); btnSource.SetIconName("document-open-symbolic"); btnSource.OnClicked += async (_, _) => { FileDialog fileDialog = FileDialog.New(); Gio.File? file; try { file = await fileDialog.SelectFolderAsync(this); } catch { file = null; } if (file != null && file.GetPath() != null) erSource.SetText(file.GetPath()!); checkButtonState(); }; erSource.AddSuffix(btnSource); Button btnDest = Button.New(); btnDest.SetValign(Align.Center); btnDest.AddCssClass("flat"); btnDest.SetIconName("document-open-symbolic"); btnDest.OnClicked += async (_, _) => { FileDialog fileDialog = FileDialog.New(); Gio.File? file; try { file = await fileDialog.SelectFolderAsync(this); } catch { file = null; } if (file != null && file.GetPath() != null) erDest.SetText(file.GetPath()!); checkButtonState(); }; erDest.AddSuffix(btnDest); PreferencesPage page = PreferencesPage.New(); PreferencesGroup pgFolders = PreferencesGroup.New(); pgFolders.SetTitle("Ordner"); pgFolders.SetDescription("Wähle einen Quell- und einen Zielordner. Es werden alle Dateien im Ordner konvertiert, jedoch keine Unterordner."); pgFolders.Add(erSource); pgFolders.Add(erDest); page.Add(pgFolders); page.Add(pgOptions); page.Add(pgBtn); return page; } public void InitInfoAlert(string title, string body) { Adw.AlertDialog alert = Adw.AlertDialog.New(title, body); alert.AddResponse("ok", "OK"); alert.SetResponseAppearance("ok", ResponseAppearance.Suggested); alert.SetDefaultResponse("ok"); alert.SetCloseResponse("ok"); alert.Present(this); } public bool InitDestructiveAlert(string title, string body, string resp) { Adw.AlertDialog alert = Adw.AlertDialog.New(title, body); alert.AddResponse("cancel", "Abbrechen"); alert.AddResponse(resp.ToLower(), resp); alert.SetResponseAppearance(resp.ToLower(), ResponseAppearance.Destructive); alert.SetDefaultResponse("cancel"); alert.SetCloseResponse("cancel"); bool running = true; bool response = false; alert.OnResponse += (Adw.AlertDialog _, Adw.AlertDialog.ResponseSignalArgs args) => { if (args.Response.Equals(resp, StringComparison.CurrentCultureIgnoreCase)) { response = true; } running = false; }; alert.Present(this); while (running == true) { } return response; } public Adw.AboutDialog InitAboutDialog() { Adw.AboutDialog aboutDialog = Adw.AboutDialog.New(); aboutDialog.SetApplicationName(AppInfo.ApplicationName); aboutDialog.SetDeveloperName(AppInfo.DeveloperName); aboutDialog.SetApplicationIcon(AppInfo.IconName); aboutDialog.SetCopyright(AppInfo.Copyright); aboutDialog.SetIssueUrl(AppInfo.IssueUrl); aboutDialog.SetWebsite(AppInfo.Website); aboutDialog.SetReleaseNotes(AppInfo.ReleaseNotes); aboutDialog.SetVersion(AppInfo.Version); aboutDialog.SetLicenseType(License.MitX11); return aboutDialog; } }