diff --git a/data/gschema.xml b/data/gschema.xml index 06881376..b2390f1a 100755 --- a/data/gschema.xml +++ b/data/gschema.xml @@ -1,32 +1,10 @@ - - - - - - "png" - - false - - - false - - - false - - - - 1 - "" - - "screen" - diff --git a/meson.build b/meson.build index b181043a..992da614 100644 --- a/meson.build +++ b/meson.build @@ -33,10 +33,9 @@ executable( icon_res, 'src/Application.vala', 'src/CaptureType.vala', + 'src/SaveDialog.vala', 'src/ScreenshotBackend.vala', 'src/ScreenshotProxy.vala', - 'src/ScreenshotWindow.vala', - 'src/Widgets/SaveDialog.vala', config_file, dependencies: [ dependency('gdk-pixbuf-2.0'), @@ -45,6 +44,7 @@ executable( dependency('gtk+-3.0'), dependency('granite', version: '>=6.0.0'), dependency('libhandy-1', version: '>=0.83.0'), + dependency('libportal'), meson.get_compiler('vala').find_library('posix'), ], install : true diff --git a/po/POTFILES b/po/POTFILES index c1ae5dca..14086608 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -1,4 +1,3 @@ src/Application.vala src/ScreenshotBackend.vala -src/ScreenshotWindow.vala -src/Widgets/SaveDialog.vala +src/SaveDialog.vala diff --git a/src/Application.vala b/src/Application.vala index 6e92ca47..9794ce09 100755 --- a/src/Application.vala +++ b/src/Application.vala @@ -20,8 +20,6 @@ public class Screenshot.Application : Gtk.Application { public const string SAVE_FOLDER = _("Screenshots"); - private ScreenshotWindow window = null; - private static bool area = false; private static bool clipboard = false; private static bool grab_pointer = false; @@ -30,6 +28,8 @@ public class Screenshot.Application : Gtk.Application { private static bool win = false; private static int delay = 1; + private Settings settings = new Settings ("io.elementary.screenshot"); + private const string CAPTURE_AREA = N_("Capture area"); private const string CAPTURE_STRING = N_("Capture the whole screen"); private const string CAPTURE_WIN = N_("Capture active window"); @@ -96,18 +96,124 @@ public class Screenshot.Application : Gtk.Application { if (area) action = 3; if (action == 0) { - if (window == null) { - window = new ScreenshotWindow (); - window.set_application (this); - window.show_all (); + take_screenshot.begin (); + } else { + take_screenshot_backend.begin (action - 1); + } + } + + private async void take_screenshot () { + hold (); + + var portal = new Xdp.Portal (); + + try { + var file_uri = yield portal.take_screenshot (null, INTERACTIVE, null); + + var pixbuf = new Gdk.Pixbuf.from_file (Filename.from_uri (file_uri, null)); + + show_save_dialog (pixbuf); + } catch (Error e) { + warning ("Failed to take screenshot via portal: %s", e.message); + } finally { + release (); + } + } + + private async void take_screenshot_backend (CaptureType capture_type) { + hold (); + + var backend = new ScreenshotBackend (); + + try { + var pixbuf = yield backend.capture (capture_type, delay, grab_pointer, redact); + + if (pixbuf != null) { + show_save_dialog (pixbuf); } + } catch (GLib.IOError.CANCELLED e) { + //Do nothing + } catch (Error e) { + show_error_dialog (e.message); + } finally { + release (); + } + } - window.present (); - } else { - window = new ScreenshotWindow.from_cmd (action, delay, grab_pointer, redact, clipboard); - window.set_application (this); - window.take_clicked (); + private void show_save_dialog (Gdk.Pixbuf pixbuf) { + var save_dialog = new SaveDialog (pixbuf, settings); + save_dialog.set_application (this); + + save_dialog.save_response.connect ((dialog, response, folder_dir, output_name, format) => { + dialog.destroy (); + + if (response) { + string[] formats = {".png", ".jpg", ".jpeg", ".bmp", ".tiff"}; + string output = output_name; + + foreach (string type in formats) { + output = output.replace (type, ""); + } + + try { + save_file (dialog.pixbuf, output, format, folder_dir); + } catch (GLib.Error e) { + show_error_dialog (e.message); + } + } else { + take_screenshot.begin (); + } + }); + + save_dialog.present (); + } + + private void save_file (Gdk.Pixbuf pixbuf, string file_name, string format, owned string folder_dir) throws GLib.Error { + if (pixbuf == null) { + critical ("Pixbuf is null"); + return; + } + + string full_file_name = ""; + string folder_from_settings = ""; + + if (folder_dir == "") { + folder_from_settings = settings.get_string ("folder-dir"); + if (folder_from_settings != "") { + folder_dir = folder_from_settings; + } else { + folder_dir = GLib.Environment.get_user_special_dir (GLib.UserDirectory.PICTURES) + + "%c".printf (GLib.Path.DIR_SEPARATOR) + Application.SAVE_FOLDER; + } + create_dir_if_missing (folder_dir); } + + int attempt = 0; + + do { + if (attempt == 0) { + full_file_name = Path.build_filename (folder_dir, "%s.%s".printf (file_name, format)); + } else { + full_file_name = Path.build_filename (folder_dir, "%s (%d).%s".printf (file_name, attempt, format)); + } + + attempt++; + } while (File.new_for_path (full_file_name).query_exists ()); + + pixbuf.save (full_file_name, format); + } + + private void show_error_dialog (string error_message) { + var dialog = new Granite.MessageDialog.with_image_from_icon_name ( + _("Could not capture screenshot"), + _("Image not saved"), + "dialog-error", + Gtk.ButtonsType.CLOSE + ); + dialog.set_application (this); + dialog.show_error_details (error_message); + dialog.response.connect (dialog.destroy); + dialog.present (); } public static void create_dir_if_missing (string path) { diff --git a/src/Widgets/SaveDialog.vala b/src/SaveDialog.vala similarity index 98% rename from src/Widgets/SaveDialog.vala rename to src/SaveDialog.vala index 20e10dd7..b7f63afe 100644 --- a/src/Widgets/SaveDialog.vala +++ b/src/SaveDialog.vala @@ -29,14 +29,13 @@ public class Screenshot.SaveDialog : Granite.Dialog { private Gtk.Label folder_name; private Gtk.Image folder_image; - public SaveDialog (Gdk.Pixbuf pixbuf, Settings settings, Gtk.Window parent) { + public SaveDialog (Gdk.Pixbuf pixbuf, Settings settings) { Object ( deletable: false, modal: true, pixbuf: pixbuf, settings: settings, - title: _("Screenshot"), - transient_for: parent + title: _("Screenshot") ); } diff --git a/src/ScreenshotWindow.vala b/src/ScreenshotWindow.vala deleted file mode 100755 index ab342d66..00000000 --- a/src/ScreenshotWindow.vala +++ /dev/null @@ -1,372 +0,0 @@ -/* -* Copyright 2017–2020 elementary, Inc. (https://elementary.io) -* 2014–2016 Fabio Zaramella -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License version 3 as published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301 USA -*/ - -public class Screenshot.ScreenshotWindow : Hdy.ApplicationWindow { - public bool to_clipboard { get; construct; } - public bool close_on_save { get; set; } - public bool mouse_pointer { get; set; } - public bool redact { get; set; } - - private Settings settings; - private CaptureType capture_mode; - private bool from_command; - private int delay; - private ScreenshotBackend backend; - private Gtk.Image all_image; - private Gtk.Label pointer_label; - private Gtk.Switch pointer_switch; - - public ScreenshotWindow () { - Object ( - resizable: false, - to_clipboard: false - ); - } - - public ScreenshotWindow.from_cmd (int action, int delay, bool grab_pointer, bool redact, bool clipboard) { - Object ( - to_clipboard: clipboard - ); - - close_on_save = true; - from_command = true; - mouse_pointer = grab_pointer; - this.delay = int.max (0, delay); - this.redact = redact; - - switch (action) { - case 1: - capture_mode = CaptureType.SCREEN; - break; - case 2: - capture_mode = CaptureType.CURRENT_WINDOW; - break; - case 3: - capture_mode = CaptureType.AREA; - break; - } - } - - construct { - if (from_command) { - return; - } - - backend = new ScreenshotBackend (); - - all_image = new Gtk.Image.from_icon_name ("grab-screen-symbolic", DND); - - var all = new Gtk.RadioButton (null) { - tooltip_text = _("Grab the whole screen") - }; - all.image = all_image; - - var curr_image = new Gtk.Image.from_icon_name ("grab-window-symbolic", DND); - - var curr_window = new Gtk.RadioButton.from_widget (all) { - tooltip_text = _("Grab the current window") - }; - curr_window.image = curr_image; - - var selection_image = new Gtk.Image.from_icon_name ("grab-area-symbolic", DND); - - var selection = new Gtk.RadioButton.from_widget (curr_window) { - tooltip_text = _("Select area to grab") - }; - selection.image = selection_image; - - pointer_label = new Gtk.Label (_("Grab pointer:")) { - halign = END - }; - - pointer_switch = new Gtk.Switch () { - halign = START - }; - - var close_label = new Gtk.Label (_("Close after saving:")) { - halign = END - }; - - var close_switch = new Gtk.Switch () { - halign = START - }; - - var redact_label = new Gtk.Label (_("Conceal text:")) { - halign = END - }; - - var redact_switch = new Gtk.Switch () { - halign = START - }; - - var delay_label = new Gtk.Label (_("Delay in seconds:")); - delay_label.halign = Gtk.Align.END; - - var delay_spin = new Gtk.SpinButton.with_range (0, 15, 1); - - var take_btn = new Gtk.Button.with_label (_("Take Screenshot")); - take_btn.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); - take_btn.can_default = true; - - this.set_default (take_btn); - - var close_btn = new Gtk.Button.with_label (_("Close")); - - var radio_box = new Gtk.Box (HORIZONTAL, 18) { - halign = CENTER - }; - radio_box.add (all); - radio_box.add (curr_window); - radio_box.add (selection); - - var option_grid = new Gtk.Grid () { - column_spacing = 12, - row_spacing = 6 - }; - option_grid.attach (pointer_label, 0, 0); - option_grid.attach (pointer_switch, 1, 0); - option_grid.attach (close_label, 0, 1); - option_grid.attach (close_switch, 1, 1); - - if (backend.can_conceal_text) { - option_grid.attach (redact_label, 0, 2); - option_grid.attach (redact_switch, 1, 2); - } - - option_grid.attach (delay_label, 0, 3); - option_grid.attach (delay_spin, 1, 3); - - var actions = new Gtk.Box (HORIZONTAL, 6) { - halign = END, - homogeneous = true - }; - actions.add (close_btn); - actions.add (take_btn); - - var box = new Gtk.Box (VERTICAL, 24) { - margin_top = 24, - margin_end = 12, - margin_bottom = 12, - margin_start = 12 - }; - box.add (radio_box); - box.add (option_grid); - box.add (actions); - - var window_handle = new Hdy.WindowHandle () { - child = box - }; - - child = window_handle; - - settings = new Settings ("io.elementary.screenshot"); - settings.bind ("mouse-pointer", pointer_switch, "active", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("mouse-pointer", this, "mouse-pointer", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("close-on-save", close_switch, "active", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("close-on-save", this, "close-on-save", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("delay", delay_spin, "value", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("redact", redact_switch, "active", GLib.SettingsBindFlags.DEFAULT); - settings.bind ("redact", this, "redact", GLib.SettingsBindFlags.DEFAULT); - - switch (settings.get_enum ("last-capture-mode")) { - case 1: - capture_mode = CaptureType.CURRENT_WINDOW; - curr_window.active = true; - break; - case 2: - capture_mode = CaptureType.AREA; - selection.active = true; - break; - default: - capture_mode = CaptureType.SCREEN; - } - - update_pointer_switch (); - - all.toggled.connect (() => { - capture_mode = CaptureType.SCREEN; - settings.set_enum ("last-capture-mode", capture_mode); - update_pointer_switch (); - }); - - curr_window.toggled.connect (() => { - capture_mode = CaptureType.CURRENT_WINDOW; - settings.set_enum ("last-capture-mode", capture_mode); - update_pointer_switch (); - }); - - selection.toggled.connect (() => { - capture_mode = CaptureType.AREA; - settings.set_enum ("last-capture-mode", capture_mode); - update_pointer_switch (); - present (); - }); - - delay_spin.value_changed.connect (() => { - delay = delay_spin.get_value_as_int (); - }); - delay = delay_spin.get_value_as_int (); - - take_btn.clicked.connect (take_clicked); - close_btn.clicked.connect (() => { - destroy (); - }); - - var gtk_settings = Gtk.Settings.get_default (); - gtk_settings.notify["gtk-application-prefer-dark-theme"].connect (() => { - update_icons (gtk_settings.gtk_application_prefer_dark_theme); - }); - - update_icons (gtk_settings.gtk_application_prefer_dark_theme); - } - - private void update_icons (bool prefers_dark) { - if (prefers_dark) { - all_image.icon_name = "grab-screen-symbolic-dark"; - } else { - all_image.icon_name = "grab-screen-symbolic"; - } - } - - private void update_pointer_switch () { - var sensitive = backend.can_screenshot_area_with_cursor || capture_mode != CaptureType.AREA; - pointer_label.sensitive = sensitive; - pointer_switch.sensitive = sensitive; - } - - private void save_file (string file_name, string format, owned string folder_dir, Gdk.Pixbuf screenshot) throws GLib.Error { - string full_file_name = ""; - string folder_from_settings = ""; - - if (folder_dir == "") { - folder_from_settings = settings.get_string ("folder-dir"); - if (folder_from_settings != "") { - folder_dir = folder_from_settings; - } else { - folder_dir = GLib.Environment.get_user_special_dir (GLib.UserDirectory.PICTURES) - + "%c".printf (GLib.Path.DIR_SEPARATOR) + Application.SAVE_FOLDER; - } - Application.create_dir_if_missing (folder_dir); - } - - int attempt = 0; - - do { - if (attempt == 0) { - full_file_name = Path.build_filename (folder_dir, "%s.%s".printf (file_name, format)); - } else { - full_file_name = Path.build_filename (folder_dir, "%s (%d).%s".printf (file_name, attempt, format)); - } - - attempt++; - } while (File.new_for_path (full_file_name).query_exists ()); - - screenshot.save (full_file_name, format); - } - - private void save_pixbuf (Gdk.Pixbuf screenshot) { - if (from_command == false) { - var save_dialog = new Screenshot.SaveDialog (screenshot, settings, this); - save_dialog.save_response.connect ((response, folder_dir, output_name, format) => { - save_dialog.destroy (); - - if (response) { - string[] formats = {".png", ".jpg", ".jpeg", ".bmp", ".tiff"}; - string output = output_name; - - foreach (string type in formats) { - output = output.replace (type, ""); - } - - try { - save_file (output, format, folder_dir, screenshot); - - if (close_on_save) { - this.destroy (); - } - } catch (GLib.Error e) { - show_error_dialog (e.message); - } - } - }); - - save_dialog.close.connect (() => { - if (close_on_save) { - this.destroy (); - } - }); - - save_dialog.present (); - } else { - if (to_clipboard) { - Gtk.Clipboard.get_default (this.get_display ()).set_image (screenshot); - } else { - var date_time = new GLib.DateTime.now_local ().format ("%Y-%m-%d %H.%M.%S"); - - /// TRANSLATORS: %s represents a timestamp here - string file_name = _("Screenshot from %s").printf (date_time); - string format = settings.get_string ("format"); - try { - save_file (file_name, format, "", screenshot); - } catch (GLib.Error e) { - show_error_dialog (e.message); - } - } - this.destroy (); - } - } - - public void take_clicked () { - hide (); - - backend.capture.begin (capture_mode, delay, mouse_pointer, redact, (obj, res) => { - Gdk.Pixbuf? pixbuf = null; - try { - pixbuf = backend.capture.end (res); - } catch (GLib.IOError.CANCELLED e) { - if (close_on_save) { - this.destroy (); - return; - } - } catch (Error e) { - show_error_dialog (e.message); - } - - if (pixbuf != null) { - save_pixbuf (pixbuf); - } - - if (from_command == false) { - present (); - } - }); - } - - private void show_error_dialog (string error_message) { - var dialog = new Granite.MessageDialog.with_image_from_icon_name ( - _("Could not capture screenshot"), - _("Image not saved"), - "dialog-error", - Gtk.ButtonsType.CLOSE - ); - dialog.show_error_details (error_message); - dialog.response.connect (destroy); - dialog.present (); - } -}