From 8eb2b297d6e03975afc17e1d468aa8913639e1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 22 Mar 2020 17:05:56 +0100 Subject: New upstream version 3.36.0 --- src/app-window.ui | 697 +++++++++++++++++++++++++++++++------------- src/app-window.vala | 257 ++++++++++++++-- src/autosave-manager.vala | 8 +- src/book-view.vala | 2 +- src/help-overlay.ui | 27 ++ src/meson.build | 1 + src/page-icon.vala | 76 +++++ src/page-view.vala | 35 +-- src/page.vala | 4 +- src/preferences-dialog.ui | 73 ++--- src/preferences-dialog.vala | 229 +-------------- src/sane-backends.vapi | 10 +- src/scanner.vala | 114 ++++++-- src/simple-scan.vala | 12 +- 14 files changed, 974 insertions(+), 571 deletions(-) create mode 100644 src/page-icon.vala (limited to 'src') diff --git a/src/app-window.ui b/src/app-window.ui index b34c07e..e0e16c0 100644 --- a/src/app-window.ui +++ b/src/app-window.ui @@ -1,12 +1,14 @@ - + True + False True + False Rotate _Left True @@ -16,6 +18,7 @@ True + False Rotate _Right True @@ -25,14 +28,17 @@ True + False _Crop True True + False True + False _None True True @@ -43,6 +49,7 @@ True + False A_4 True True @@ -53,6 +60,7 @@ True + False A_5 True True @@ -63,6 +71,7 @@ True + False A_6 True True @@ -73,6 +82,7 @@ True + False _Letter True True @@ -83,6 +93,7 @@ True + False Le_gal True True @@ -93,6 +104,7 @@ True + False 4×6 True True @@ -103,6 +115,7 @@ True + False A_3 True True @@ -113,6 +126,7 @@ True + False _Custom True True @@ -123,12 +137,14 @@ True + False True False + False _Rotate Crop True @@ -141,6 +157,7 @@ True + False Move Left @@ -149,6 +166,7 @@ True + False Move Right True @@ -160,6 +178,7 @@ gtk-copy True False + False True True @@ -170,6 +189,7 @@ gtk-delete True + False True True @@ -177,147 +197,467 @@ - - diff --git a/src/app-window.vala b/src/app-window.vala index 02f2072..6f2d353 100644 --- a/src/app-window.vala +++ b/src/app-window.vala @@ -44,6 +44,9 @@ public class AppWindow : Gtk.ApplicationWindow private PreferencesDialog preferences_dialog; + private bool setting_devices; + private bool user_selected_device; + [GtkChild] private Gtk.HeaderBar header_bar; [GtkChild] @@ -53,6 +56,10 @@ public class AppWindow : Gtk.ApplicationWindow [GtkChild] private Gtk.Label status_primary_label; [GtkChild] + private Gtk.ListStore device_model; + [GtkChild] + private Gtk.ComboBox device_combo; + [GtkChild] private Gtk.Label status_secondary_label; [GtkChild] private Gtk.Box main_vbox; @@ -98,6 +105,8 @@ public class AppWindow : Gtk.ApplicationWindow [GtkChild] private Gtk.Image scan_options_image; [GtkChild] + private Gtk.Image scan_hint_image; + [GtkChild] private Gtk.RadioButton scan_single_radio; [GtkChild] private Gtk.RadioButton scan_adf_radio; @@ -177,12 +186,6 @@ public class AppWindow : Gtk.ApplicationWindow set { preferences_dialog.set_page_delay (value); } } - public string? selected_device - { - owned get { return preferences_dialog.get_selected_device (); } - set { preferences_dialog.set_selected_device (value); } - } - public signal void start_scan (string? device, ScanOptions options); public signal void stop_scan (); @@ -190,6 +193,11 @@ public class AppWindow : Gtk.ApplicationWindow { settings = new Settings ("org.gnome.SimpleScan"); + var renderer = new Gtk.CellRendererText (); + renderer.set_property ("xalign", 0.5); + device_combo.pack_start (renderer, true); + device_combo.add_attribute (renderer, "text", 1); + book = new Book (); book.page_added.connect (page_added_cb); book.reordered.connect (reordered_cb); @@ -199,19 +207,6 @@ public class AppWindow : Gtk.ApplicationWindow load (); clear_document (); - autosave_manager = new AutosaveManager (); - autosave_manager.book = book; - autosave_manager.load (); - - if (book.n_pages == 0) - book_needs_saving = false; - else - { - stack.set_visible_child_name ("document"); - book_view.selected_page = book.get_page (0); - book_needs_saving = true; - book_changed_cb (book); - } } ~AppWindow () @@ -255,13 +250,15 @@ public class AppWindow : Gtk.ApplicationWindow status_primary_label.set_text (/* Label shown when searching for scanners */ _("Searching for Scanners…")); status_secondary_label.visible = false; + device_combo.visible = false; } - else if (selected_device != null) + else if (get_selected_device () != null) { status_primary_label.set_text (/* Label shown when detected a scanner */ _("Ready to Scan")); - status_secondary_label.set_text (preferences_dialog.get_selected_device_label ()); - status_secondary_label.visible = true; + status_secondary_label.set_text (get_selected_device_label ()); + status_secondary_label.visible = false; + device_combo.visible = true; } else if (this.missing_driver != null) { @@ -270,6 +267,7 @@ public class AppWindow : Gtk.ApplicationWindow /* Instructions to install driver software */ status_secondary_label.set_markup (_("You need to install driver software for your scanner.")); status_secondary_label.visible = true; + device_combo.visible = false; } else { @@ -278,6 +276,7 @@ public class AppWindow : Gtk.ApplicationWindow /* Hint to user on why there are no scanners detected */ status_secondary_label.set_text (_("Please check your scanner is connected and powered on")); status_secondary_label.visible = true; + device_combo.visible = false; } } @@ -285,10 +284,162 @@ public class AppWindow : Gtk.ApplicationWindow { have_devices = true; this.missing_driver = missing_driver; - preferences_dialog.set_scan_devices (devices); + + setting_devices = true; + + /* If the user hasn't chosen a scanner choose the best available one */ + var have_selection = false; + if (user_selected_device) + have_selection = device_combo.active >= 0; + + /* Add new devices */ + int index = 0; + Gtk.TreeIter iter; + foreach (var device in devices) + { + int n_delete = -1; + + /* Find if already exists */ + if (device_model.iter_nth_child (out iter, null, index)) + { + int i = 0; + do + { + string name; + bool matched; + + device_model.get (iter, 0, out name, -1); + matched = name == device.name; + + if (matched) + { + n_delete = i; + break; + } + i++; + } while (device_model.iter_next (ref iter)); + } + + /* If exists, remove elements up to this one */ + if (n_delete >= 0) + { + int i; + + /* Update label */ + device_model.set (iter, 1, device.label, -1); + + for (i = 0; i < n_delete; i++) + { + device_model.iter_nth_child (out iter, null, index); +#if VALA_0_36 + device_model.remove (ref iter); +#else + device_model.remove (iter); +#endif + } + } + else + { + device_model.insert (out iter, index); + device_model.set (iter, 0, device.name, 1, device.label, -1); + } + index++; + } + + /* Remove any remaining devices */ + while (device_model.iter_nth_child (out iter, null, index)) +#if VALA_0_36 + device_model.remove (ref iter); +#else + device_model.remove (iter); +#endif + + /* Select the previously selected device or the first available device */ + if (!have_selection) + { + var device = settings.get_string ("selected-device"); + if (device != null && find_scan_device (device, out iter)) + device_combo.set_active_iter (iter); + else + device_combo.set_active (0); + } + + setting_devices = false; + update_scan_status (); } + private bool prompt_to_load_autosaved_book () + { + var dialog = new Gtk.MessageDialog (this, + Gtk.DialogFlags.MODAL, + Gtk.MessageType.QUESTION, + Gtk.ButtonsType.YES_NO, + /* Contents of dialog that shows if autosaved book should be loaded. */ + _("An autosaved book exists. Do you want to open it?")); + dialog.set_default_response(Gtk.ResponseType.YES); + var response = dialog.run (); + dialog.destroy (); + return response == Gtk.ResponseType.YES; + } + + private string? get_selected_device () + { + Gtk.TreeIter iter; + + if (device_combo.get_active_iter (out iter)) + { + string device; + device_model.get (iter, 0, out device, -1); + return device; + } + + return null; + } + + private string? get_selected_device_label () + { + Gtk.TreeIter iter; + + if (device_combo.get_active_iter (out iter)) + { + string label; + device_model.get (iter, 1, out label, -1); + return label; + } + + return null; + } + + public void set_selected_device (string device) + { + user_selected_device = true; + + Gtk.TreeIter iter; + if (!find_scan_device (device, out iter)) + return; + + device_combo.set_active_iter (iter); + } + + private bool find_scan_device (string device, out Gtk.TreeIter iter) + { + bool have_iter = false; + + if (device_model.get_iter_first (out iter)) + { + do + { + string d; + device_model.get (iter, 0, out d, -1); + if (d == device) + have_iter = true; + } while (!have_iter && device_model.iter_next (ref iter)); + } + + return have_iter; + } + private string? choose_file_location () { /* Get directory to save to */ @@ -543,7 +694,7 @@ public class AppWindow : Gtk.ApplicationWindow private async bool prompt_to_save_async (string title, string discard_label) { - if (!book_needs_saving) + if (!book_needs_saving || (book.n_pages == 0)) return true; var dialog = new Gtk.MessageDialog (this, @@ -624,7 +775,7 @@ public class AppWindow : Gtk.ApplicationWindow { status_primary_label.set_text (/* Label shown when scan started */ _("Contacting scanner…")); - start_scan (selected_device, options); + start_scan (get_selected_device (), options); } private void scan_single_cb () @@ -733,10 +884,12 @@ public class AppWindow : Gtk.ApplicationWindow if (document_hint == "text") { text_radio.active = true; + scan_hint_image.icon_name = "x-office-document-symbolic"; } else if (document_hint == "photo") { photo_radio.active = true; + scan_hint_image.icon_name = "image-x-generic-symbolic"; } if (save) @@ -757,6 +910,12 @@ public class AppWindow : Gtk.ApplicationWindow set_document_hint ("photo", true); } + [GtkCallback] + private void preferences_button_clicked_cb (Gtk.Button button) + { + preferences_dialog.present (); + } + private ScanOptions make_scan_options () { var options = new ScanOptions (); @@ -780,9 +939,21 @@ public class AppWindow : Gtk.ApplicationWindow return options; } + [GtkCallback] + private void device_combo_changed_cb (Gtk.Widget widget) + { + if (setting_devices) + return; + user_selected_device = true; + if (get_selected_device () != null) + settings.set_string ("selected-device", get_selected_device ()); + } + [GtkCallback] private void scan_button_clicked_cb (Gtk.Widget widget) { + scan_button.visible = false; + stop_button.visible = true; var options = make_scan_options (); options.type = scan_type; if (options.type == ScanType.ADF_BOTH) @@ -793,6 +964,8 @@ public class AppWindow : Gtk.ApplicationWindow [GtkCallback] private void stop_scan_button_clicked_cb (Gtk.Widget widget) { + scan_button.visible = true; + stop_button.visible = false; stop_scan (); } @@ -1609,15 +1782,17 @@ public class AppWindow : Gtk.ApplicationWindow app.set_accels_for_action ("app.print", { "P" }); app.set_accels_for_action ("app.help", { "F1" }); app.set_accels_for_action ("app.quit", { "Q" }); + app.set_accels_for_action ("win.show-help-overlay", { "F1" }); var gear_menu = new Menu (); var section = new Menu (); gear_menu.append_section (null, section); section.append (_("Email"), "app.email"); + section.append (_("Print"), "app.print"); section.append (C_("menu", "Reorder Pages"), "app.reorder"); + section.append (_("Preferences"), "app.preferences"); section = new Menu (); gear_menu.append_section (null, section); - section.append (_("Preferences"), "app.preferences"); section.append (_("Keyboard Shortcuts"), "win.show-help-overlay"); section.append (_("Help"), "app.help"); section.append (_("About Document Scanner"), "app.about"); @@ -1750,6 +1925,20 @@ public class AppWindow : Gtk.ApplicationWindow window_height = 400; window_is_maximized = state_get_boolean (f, "window", "is-maximized"); window_is_fullscreen = state_get_boolean (f, "window", "is-fullscreen"); + scan_type = Scanner.type_from_string(state_get_string (f, "scanner", "scan-type", "single")); + set_scan_type (scan_type); + } + + private string state_get_string (KeyFile f, string group_name, string key, string default) + { + try + { + return f.get_string (group_name, key); + } + catch + { + return default; + } } private int state_get_integer (KeyFile f, string group_name, string key, int default = 0) @@ -1798,6 +1987,7 @@ public class AppWindow : Gtk.ApplicationWindow f.set_integer ("window", "height", window_height); f.set_boolean ("window", "is-maximized", window_is_maximized); f.set_boolean ("window", "is-fullscreen", window_is_fullscreen); + f.set_string ("scanner", "scan-type", Scanner.type_to_string(scan_type)); try { FileUtils.set_contents (state_filename, f.to_data ()); @@ -1811,6 +2001,21 @@ public class AppWindow : Gtk.ApplicationWindow public void start () { visible = true; + autosave_manager = new AutosaveManager (); + autosave_manager.book = book; + + if (autosave_manager.exists () && prompt_to_load_autosaved_book ()) + autosave_manager.load (); + + if (book.n_pages == 0) + book_needs_saving = false; + else + { + stack.set_visible_child_name ("document"); + book_view.selected_page = book.get_page (0); + book_needs_saving = true; + book_changed_cb (book); + } } } diff --git a/src/autosave-manager.vala b/src/autosave-manager.vala index 84a1e00..c5eb65e 100644 --- a/src/autosave-manager.vala +++ b/src/autosave-manager.vala @@ -59,6 +59,12 @@ public class AutosaveManager page_filenames = new HashTable (direct_hash, direct_equal); } + public bool exists () + { + var file = File.new_for_path (AUTOSAVE_PATH); + return file.query_exists (); + } + public void load () { debug ("Loading autosave information"); @@ -328,7 +334,7 @@ public class AutosaveManager file.set_integer (page_name, "crop-height", page.crop_height); } file.set_value ("simple-scan", "pages", page_names); - + try { DirUtils.create_with_parents (AUTOSAVE_DIR, 0777); diff --git a/src/book-view.vala b/src/book-view.vala index 782a011..12da06f 100644 --- a/src/book-view.vala +++ b/src/book-view.vala @@ -34,7 +34,7 @@ public class BookView : Gtk.Box else return null; } - set + set { if (selected_page == value) return; diff --git a/src/help-overlay.ui b/src/help-overlay.ui index dabec9f..b1a0127 100644 --- a/src/help-overlay.ui +++ b/src/help-overlay.ui @@ -122,6 +122,33 @@ + + + 1 + General + + + 1 + F1 + Show help + + + + + 1 + <ctrl>F1 + Keyboard shortcuts + + + + + 1 + <ctrl>q + Quit + + + + diff --git a/src/meson.build b/src/meson.build index 7d535c8..419ed06 100644 --- a/src/meson.build +++ b/src/meson.build @@ -24,6 +24,7 @@ simple_scan = executable ('simple-scan', 'book.vala', 'book-view.vala', 'page.vala', + 'page-icon.vala', 'page-view.vala', 'preferences-dialog.vala', 'simple-scan.vala', diff --git a/src/page-icon.vala b/src/page-icon.vala new file mode 100644 index 0000000..793ca5b --- /dev/null +++ b/src/page-icon.vala @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-2017 Canonical Ltd. + * Author: Robert Ancell , + * Eduard Gotwig + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +public class PageIcon : Gtk.DrawingArea +{ + private string text; + private double r; + private double g; + private double b; + private const int MINIMUM_WIDTH = 20; + + public PageIcon (string text, double r = 1.0, double g = 1.0, double b = 1.0) + { + this.text = text; + this.r = r; + this.g = g; + this.b = b; + } + + public override void get_preferred_width (out int minimum_width, out int natural_width) + { + minimum_width = natural_width = MINIMUM_WIDTH; + } + + public override void get_preferred_height (out int minimum_height, out int natural_height) + { + minimum_height = natural_height = (int) Math.round (MINIMUM_WIDTH * Math.SQRT2); + } + + public override void get_preferred_height_for_width (int width, out int minimum_height, out int natural_height) + { + minimum_height = natural_height = (int) (width * Math.SQRT2); + } + + public override void get_preferred_width_for_height (int height, out int minimum_width, out int natural_width) + { + minimum_width = natural_width = (int) (height / Math.SQRT2); + } + + public override bool draw (Cairo.Context c) + { + var w = get_allocated_width (); + var h = get_allocated_height (); + if (w * Math.SQRT2 > h) + w = (int) Math.round (h / Math.SQRT2); + else + h = (int) Math.round (w * Math.SQRT2); + + c.translate ((get_allocated_width () - w) / 2, (get_allocated_height () - h) / 2); + + c.rectangle (0.5, 0.5, w - 1, h - 1); + + c.set_source_rgb (r, g, b); + c.fill_preserve (); + + c.set_line_width (1.0); + c.set_source_rgb (0.0, 0.0, 0.0); + c.stroke (); + + Cairo.TextExtents extents; + c.text_extents (text, out extents); + c.translate ((w - extents.width) * 0.5 - 0.5, (h + extents.height) * 0.5 - 0.5); + c.show_text (text); + + return true; + } +} diff --git a/src/page-view.vala b/src/page-view.vala index abe5e69..91a2c82 100644 --- a/src/page-view.vala +++ b/src/page-view.vala @@ -36,7 +36,7 @@ public class PageView : Object public bool selected { get { return selected_; } - set + set { if ((this.selected && selected) || (!this.selected && !selected)) return; @@ -845,39 +845,6 @@ public class PageView : Object Gdk.cairo_set_source_pixbuf (context, image, 0, 0); context.paint (); - /* Draw throbber */ - if (page.is_scanning && !page.has_data) - { - double outer_radius; - if (w > h) - outer_radius = 0.15 * w; - else - outer_radius = 0.15 * h; - var arc = Math.PI / animate_n_segments; - - /* Space circles */ - var x = outer_radius * Math.sin (arc); - var y = outer_radius * (Math.cos (arc) - 1.0); - var inner_radius = 0.6 * Math.sqrt (x*x + y*y); - - double offset = 0.0; - for (var i = 0; i < animate_n_segments; i++, offset += arc * 2) - { - x = w / 2 + outer_radius * Math.sin (offset); - y = h / 2 - outer_radius * Math.cos (offset); - context.arc (x, y, inner_radius, 0, 2 * Math.PI); - - if (i == animate_segment) - { - context.set_source_rgb (0.75, 0.75, 0.75); - context.fill_preserve (); - } - - context.set_source_rgb (0.5, 0.5, 0.5); - context.stroke (); - } - } - /* Draw scan line */ if (page.is_scanning && page.scan_line > 0) { diff --git a/src/page.vala b/src/page.vala index 026fdcc..c6b532e 100644 --- a/src/page.vala +++ b/src/page.vala @@ -90,7 +90,7 @@ public class Page : Object public ScanDirection scan_direction { get { return scan_direction_; } - + set { if (scan_direction_ == value) @@ -371,7 +371,7 @@ public class Page : Object { return_if_fail (width >= 1); return_if_fail (height >= 1); - + if (crop_name == null && has_crop && crop_width == width && crop_height == height) return; crop_name = null; diff --git a/src/preferences-dialog.ui b/src/preferences-dialog.ui index 75d8a4c..63d06e0 100644 --- a/src/preferences-dialog.ui +++ b/src/preferences-dialog.ui @@ -14,14 +14,6 @@ 1 10 - - - - - - - - @@ -74,40 +66,12 @@ True 15 10 - - - True - _Scanner - True - device_combo - 1 - - - - 0 - 0 - - - - - True - True - device_model - - - - 1 - 0 - - True - Scan Sides + Scan _Sides True - scan_side_box + front_side_button 1