diff --git a/data/gschema.xml b/data/gschema.xml index 26bc8d0..f0586d3 100644 --- a/data/gschema.xml +++ b/data/gschema.xml @@ -1,10 +1,15 @@ - + + false + Whether indicator is hidden and inactive + Always hide indicator if set to true. While hidden the indicator does not monitor the clipboard. + + true - Whether indicator is visible - Hide indicator if set to false. It is still running, just hidden + Whether indicator is active + Whether the indicator is monitoring and recording the clipboard contents diff --git a/src/HistoryWidget.vala b/src/HistoryWidget.vala index d2210f6..3226a34 100644 --- a/src/HistoryWidget.vala +++ b/src/HistoryWidget.vala @@ -4,27 +4,63 @@ */ public class Clipboard.HistoryWidget : Gtk.Box { + private const string ACTIVE_DESCRIPTION = N_("Monitoring the clipboard contents"); + private const string PRIVACY_DESCRIPTION = N_("Privacy Mode is On"); + private Gee.HashSet clipboard_text_set; private Gtk.ListBox clipboard_item_list; private string last_text = ""; private uint wait_timeout = 0; + private Granite.SwitchModelButton active_switch; + private Gtk.Box privacy_widget; + private Gtk.ScrolledWindow scroll_box; + private Gtk.Stack stack; + private unowned Gtk.Clipboard clipboard; public signal void close_request (); construct { + orientation = VERTICAL; + spacing = 6; + + active_switch = new Granite.SwitchModelButton (_("Clipboard Manager")) { + description = _(ACTIVE_DESCRIPTION) + }; + + var inactive_header_label = new Granite.HeaderLabel (_("The ClipboardManager is disabled")); + var inactive_subheader_label = new Gtk.Label ("") { + label = Granite.TOOLTIP_SECONDARY_TEXT_MARKUP.printf (_("History is off in the Privacy and Security settings")), + use_markup = true + }; + privacy_widget = new Gtk.Box (VERTICAL, 0) { + margin_start = 6, + margin_end = 6 + }; + privacy_widget.add (inactive_header_label); + privacy_widget.add (inactive_subheader_label); + + Clipboard.Indicator.settings.bind ("active", active_switch, "active", DEFAULT); + clipboard_text_set = new Gee.HashSet (); clipboard_item_list = new Gtk.ListBox () { selection_mode = SINGLE }; clipboard_item_list.set_placeholder (new Gtk.Label (_("Clipboard Empty"))); - var scroll_box = new Gtk.ScrolledWindow (null, null); + + scroll_box = new Gtk.ScrolledWindow (null, null); scroll_box.max_content_height = 512; scroll_box.propagate_natural_height = true; scroll_box.hscrollbar_policy = Gtk.PolicyType.NEVER; scroll_box.add (clipboard_item_list); - add (scroll_box); + stack = new Gtk.Stack (); + stack.add_named (scroll_box, "clipboard"); + stack.add_named (privacy_widget, "privacy"); + + add (active_switch); + add (stack); + show_all (); clipboard_item_list.row_activated.connect ((row) => { @@ -33,6 +69,8 @@ public class Clipboard.HistoryWidget : Gtk.Box { clipboard.set_text (text, -1); close_request (); }); + + clipboard = Gtk.Clipboard.get_default (Gdk.Display.get_default ()); } ~HistoryWidget () { @@ -41,28 +79,43 @@ public class Clipboard.HistoryWidget : Gtk.Box { // No notifications from clipboard? So poll it periodically for new text public void wait_for_text () { - var clipboard = Gtk.Clipboard.get_default (Gdk.Display.get_default ()); - wait_timeout = Timeout.add_full (Priority.LOW, 1000, () => { - if (clipboard.wait_is_text_available ()) { - clipboard.request_text ((cb, text) => { - if (text != last_text && !clipboard_text_set.contains (text)) { - last_text = text; - clipboard_text_set.add (text); - var new_item = new ItemRow (text); - clipboard_item_list.prepend (new_item); - clipboard_item_list.select_row (new_item); - clipboard_item_list.show_all (); - } - }); - } - - return Source.CONTINUE; + clipboard.owner_change.connect (on_clipboard_owner_change); + } + + private void on_clipboard_owner_change () requires (clipboard != null) { + if (clipboard.wait_is_text_available ()) { + clipboard.request_text ((cb, text) => { + if (!clipboard_text_set.contains (text)) { + clipboard_text_set.add (text); + var new_item = new ItemRow (text); + clipboard_item_list.prepend (new_item); + clipboard_item_list.select_row (new_item); + } + }); + } + } + + public void stop_waiting_for_text () requires (clipboard != null) { + clipboard.owner_change.disconnect (on_clipboard_owner_change); + } + + public void clear_history () { + clipboard_text_set.clear (); + clipboard_item_list.@foreach ((child) => { + child.destroy (); }); } - public void stop_waiting_for_text () { - if (wait_timeout > 0) { - Source.remove (wait_timeout); + public void set_privacy_mode (bool privacy_on) { + active_switch.sensitive = !privacy_on; + stack.visible_child_name = privacy_on ? "privacy" : "clipboard"; + if (privacy_on) { + stop_waiting_for_text (); + clear_history (); + active_switch.description = _(PRIVACY_DESCRIPTION); + } else { + wait_for_text (); + active_switch.description = _(ACTIVE_DESCRIPTION); } } diff --git a/src/Indicator.vala b/src/Indicator.vala index 3824488..4d3e75d 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -4,10 +4,17 @@ */ public class Clipboard.Indicator : Wingpanel.Indicator { - private static GLib.Settings settings; + public static GLib.Settings settings; + private static GLib.Settings gnome_privacy_settings; + private const string NORMAL_ICON_NAME = "edit-copy-symbolic"; + private const string STOPPED_ICON_NAME = "task-past-due-symbolic"; private Gtk.Image panel_icon; private HistoryWidget history_widget; + public bool always_hide { get; set; } + public bool active { get; set; } + public bool privacy_on { get; set; } + public Wingpanel.IndicatorManager.ServerType server_type { get; construct set; } public Indicator (Wingpanel.IndicatorManager.ServerType indicator_server_type) { @@ -21,45 +28,70 @@ public class Clipboard.Indicator : Wingpanel.Indicator { Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); Intl.textdomain (GETTEXT_PACKAGE); + visible = true; + settings = new GLib.Settings ("io.github.ellie_commons.indicator-clipboard"); - settings.bind ("visible", this, "visible", GLib.SettingsBindFlags.DEFAULT); + settings.bind ("always-hide", this, "always-hide", DEFAULT); + settings.bind ("active", this, "active", DEFAULT); + + + // Ensure correct appearance before showing + get_display_widget (); + get_widget (); + update_appearance (); } + public override Gtk.Widget get_display_widget () { if (panel_icon == null) { - panel_icon = new Gtk.Image.from_icon_name ("edit-copy-symbolic", Gtk.IconSize.SMALL_TOOLBAR); + panel_icon = new Gtk.Image.from_icon_name (NORMAL_ICON_NAME, Gtk.IconSize.SMALL_TOOLBAR); if (server_type == Wingpanel.IndicatorManager.ServerType.GREETER) { this.visible = false; } else { - // var visible_settings = new Settings ("io.elementary.desktop.wingpanel.clipboard"); - // visible_settings.bind ("show-indicator", this, "visible", SettingsBindFlags.DEFAULT); - this.visible = true; + gnome_privacy_settings = new Settings ("org.gnome.desktop.privacy"); + gnome_privacy_settings.bind ("remember-recent-files", this, "privacy-on", DEFAULT | INVERT_BOOLEAN); } } - get_widget (); return panel_icon; } public override Gtk.Widget? get_widget () { if (history_widget == null && server_type == Wingpanel.IndicatorManager.ServerType.SESSION) { - history_widget = new HistoryWidget (); - history_widget.close_request.connect (() => { - close (); - }); - history_widget.wait_for_text (); + + history_widget = new HistoryWidget (); + history_widget.close_request.connect (() => { + close (); + }); + + this.notify["privacy-on"].connect (update_appearance); + this.notify["always-hide"].connect (update_appearance); + this.notify["active"].connect (update_appearance); + update_appearance (); } return history_widget; } public override void opened () { + } public override void closed () { } + + private void update_appearance () { + if (!active || privacy_on || always_hide) { + panel_icon.icon_name = STOPPED_ICON_NAME; + } else if (active && !privacy_on && !always_hide) { + panel_icon.icon_name = NORMAL_ICON_NAME; + } + + visible = !always_hide; + history_widget.set_privacy_mode (privacy_on); + } } public Wingpanel.Indicator? get_indicator (Module module, Wingpanel.IndicatorManager.ServerType server_type) {