Blame editor/dconf-view.vala

Packit Service f2b131
/*
Packit Service f2b131
  This file is part of Dconf Editor
Packit Service f2b131
Packit Service f2b131
  Dconf Editor is free software: you can redistribute it and/or modify
Packit Service f2b131
  it under the terms of the GNU General Public License as published by
Packit Service f2b131
  the Free Software Foundation, either version 3 of the License, or
Packit Service f2b131
  (at your option) any later version.
Packit Service f2b131
Packit Service f2b131
  Dconf Editor is distributed in the hope that it will be useful,
Packit Service f2b131
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service f2b131
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service f2b131
  GNU General Public License for more details.
Packit Service f2b131
Packit Service f2b131
  You should have received a copy of the GNU General Public License
Packit Service f2b131
  along with Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
Packit Service f2b131
*/
Packit Service f2b131
Packit Service f2b131
using Gtk;
Packit Service f2b131
Packit Service f2b131
public interface KeyEditorChild : Widget
Packit Service f2b131
{
Packit Service f2b131
    public signal void value_has_changed (bool is_valid = true);
Packit Service f2b131
Packit Service f2b131
    public abstract Variant get_variant ();
Packit Service f2b131
    public signal void child_activated ();
Packit Service f2b131
Packit Service f2b131
    public abstract void reload (Variant gvariant);
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildSingle : Label, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildSingle (Variant key_value, string text)
Packit Service f2b131
    {
Packit Service f2b131
        variant = key_value;
Packit Service f2b131
        set_label (text);
Packit Service f2b131
        show ();
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant) {}
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildEnum : MenuButton, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
    private GLib.Action action;
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildEnum (Variant initial_value, bool delay_mode, bool has_planned_change, Variant range_content)
Packit Service f2b131
    {
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.halign = Align.START;
Packit Service f2b131
        this.use_popover = true;
Packit Service f2b131
        this.width_request = 100;
Packit Service f2b131
Packit Service f2b131
        ContextPopover popover = new ContextPopover ();
Packit Service f2b131
        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "<enum>", initial_value, range_content);
Packit Service f2b131
        popover.set_relative_to (this);
Packit Service f2b131
Packit Service f2b131
        popover.value_changed.connect ((gvariant) => {
Packit Service f2b131
                if (gvariant == null)   // TODO better (1/3)
Packit Service f2b131
                    assert_not_reached ();
Packit Service f2b131
                reload ((!) gvariant);
Packit Service f2b131
                popover.closed ();
Packit Service f2b131
Packit Service f2b131
                value_has_changed ();
Packit Service f2b131
            });
Packit Service f2b131
        reload (initial_value);
Packit Service f2b131
        this.set_popover ((Popover) popover);
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        variant = gvariant;
Packit Service f2b131
        VariantType type = gvariant.get_type ();
Packit Service f2b131
        label = type == VariantType.STRING ? gvariant.get_string () : gvariant.print (false);
Packit Service f2b131
        action.change_state (new Variant.maybe (null, new Variant.maybe (type, gvariant)));
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildFlags : Grid, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private string [] all_flags;
Packit Service f2b131
    private ContextPopover popover = new ContextPopover ();
Packit Service f2b131
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
    private Label label = new Label ("");
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildFlags (Variant initial_value, string [] _all_flags, string [] active_flags)
Packit Service f2b131
    {
Packit Service f2b131
        all_flags = _all_flags;
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.orientation = Orientation.HORIZONTAL;
Packit Service f2b131
        this.column_spacing = 8;
Packit Service f2b131
Packit Service f2b131
        MenuButton button = new MenuButton ();
Packit Service f2b131
        button.visible = true;
Packit Service f2b131
        button.use_popover = true;
Packit Service f2b131
        button.halign = Align.START;
Packit Service f2b131
        button.get_style_context ().add_class ("image-button");
Packit Service f2b131
        this.add (button);
Packit Service f2b131
Packit Service f2b131
        label.visible = true;
Packit Service f2b131
        label.halign = Align.START;
Packit Service f2b131
        label.hexpand = true;
Packit Service f2b131
        this.add (label);
Packit Service f2b131
Packit Service f2b131
        popover.create_flags_list (active_flags, all_flags);
Packit Service f2b131
        popover.set_relative_to (button);
Packit Service f2b131
        popover.value_changed.connect ((gvariant) => {
Packit Service f2b131
                if (gvariant == null)   // TODO better (2/3)
Packit Service f2b131
                    assert_not_reached ();
Packit Service f2b131
                reload ((!) gvariant);
Packit Service f2b131
                value_has_changed ();
Packit Service f2b131
            });
Packit Service f2b131
        reload (initial_value);
Packit Service f2b131
        button.set_popover ((Popover) popover);
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void update_flags (string [] active_flags)
Packit Service f2b131
    {
Packit Service f2b131
        foreach (string flag in all_flags)
Packit Service f2b131
            popover.update_flag_status (flag, flag in active_flags);
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        this.variant = gvariant;
Packit Service f2b131
        label.label = gvariant.print (false);
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildNullableBool : MenuButton, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
    private Variant? maybe_variant;
Packit Service f2b131
    private GLib.Action action;
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildNullableBool (Variant initial_value, bool delay_mode, bool has_planned_change, Variant? range_content_or_null)
Packit Service f2b131
    {
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.halign = Align.START;
Packit Service f2b131
        this.use_popover = true;
Packit Service f2b131
        this.width_request = 100;
Packit Service f2b131
Packit Service f2b131
        ContextPopover popover = new ContextPopover ();
Packit Service f2b131
        action = popover.create_buttons_list (false, delay_mode, has_planned_change, "mb", initial_value, range_content_or_null);
Packit Service f2b131
        popover.set_relative_to (this);
Packit Service f2b131
Packit Service f2b131
        popover.value_changed.connect ((gvariant) => {
Packit Service f2b131
                if (gvariant == null)   // TODO better (3/3)
Packit Service f2b131
                    assert_not_reached ();
Packit Service f2b131
                reload ((!) gvariant);
Packit Service f2b131
                popover.closed ();
Packit Service f2b131
Packit Service f2b131
                value_has_changed ();
Packit Service f2b131
            });
Packit Service f2b131
        reload (initial_value);
Packit Service f2b131
        this.set_popover ((Popover) popover);
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        variant = gvariant;
Packit Service f2b131
        maybe_variant = variant.get_maybe ();
Packit Service f2b131
Packit Service f2b131
        if (maybe_variant == null)
Packit Service f2b131
            label = Key.cool_boolean_text_value (null);
Packit Service f2b131
        else
Packit Service f2b131
            label = Key.cool_boolean_text_value (((!) maybe_variant).get_boolean ());
Packit Service f2b131
Packit Service f2b131
        action.change_state (new Variant.maybe (null, new Variant.maybe (new VariantType ("mb"), gvariant)));
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildBool : Box, KeyEditorChild // might be managed by action, but can't find a way to ensure one-and-only-one button is active  // https://bugzilla.gnome.org/show_bug.cgi?id=769876
Packit Service f2b131
{
Packit Service f2b131
    private ToggleButton button_true;
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildBool (bool initial_value)
Packit Service f2b131
    {
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.orientation = Orientation.HORIZONTAL;
Packit Service f2b131
        this.halign = Align.START;
Packit Service f2b131
        this.homogeneous = true;
Packit Service f2b131
        this.width_request = 100;
Packit Service f2b131
        this.get_style_context ().add_class ("linked");
Packit Service f2b131
Packit Service f2b131
        ToggleButton button_false = new ToggleButton ();
Packit Service f2b131
        button_false.visible = true;
Packit Service f2b131
        button_false.label = Key.cool_boolean_text_value (false);
Packit Service f2b131
        this.add (button_false);
Packit Service f2b131
Packit Service f2b131
        button_true = new ToggleButton ();
Packit Service f2b131
        button_true.visible = true;
Packit Service f2b131
        button_true.label = Key.cool_boolean_text_value (true);
Packit Service f2b131
        this.add (button_true);
Packit Service f2b131
Packit Service f2b131
        button_true.active = initial_value;
Packit Service f2b131
        button_true.bind_property ("active", button_false, "active", BindingFlags.INVERT_BOOLEAN|BindingFlags.SYNC_CREATE|BindingFlags.BIDIRECTIONAL);
Packit Service f2b131
Packit Service f2b131
        button_true.toggled.connect (() => value_has_changed ());
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return new Variant.boolean (button_true.active);
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        button_true.active = gvariant.get_boolean ();
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildNumberDouble : Entry, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
Packit Service f2b131
    private ulong deleted_text_handler = 0;
Packit Service f2b131
    private ulong inserted_text_handler = 0;
Packit Service f2b131
Packit Service f2b131
    construct
Packit Service f2b131
    {
Packit Service f2b131
        get_style_context ().add_class ("key-editor-child-entry");
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildNumberDouble (Variant initial_value)
Packit Service f2b131
    {
Packit Service f2b131
        this.variant = initial_value;
Packit Service f2b131
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.secondary_icon_activatable = false;
Packit Service f2b131
        this.set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("Failed to parse as double."));
Packit Service f2b131
Packit Service f2b131
        this.text = initial_value.print (false);
Packit Service f2b131
Packit Service f2b131
        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
Packit Service f2b131
        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
Packit Service f2b131
        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
Packit Service f2b131
        ulong entry_activate_handler = activate.connect (() => { if (test_value ()) child_activated (); });
Packit Service f2b131
Packit Service f2b131
        destroy.connect (() => {
Packit Service f2b131
                ref_buffer.disconnect (deleted_text_handler);
Packit Service f2b131
                ref_buffer.disconnect (inserted_text_handler);
Packit Service f2b131
                disconnect (entry_activate_handler);
Packit Service f2b131
            });
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private bool test_value ()
Packit Service f2b131
    {
Packit Service f2b131
        string tmp_text = this.text; // don't put in the try{} for correct C code
Packit Service f2b131
        try
Packit Service f2b131
        {
Packit Service f2b131
            Variant? tmp_variant = Variant.parse (VariantType.DOUBLE, tmp_text);
Packit Service f2b131
            variant = (!) tmp_variant;
Packit Service f2b131
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (context.has_class ("error"))
Packit Service f2b131
                context.remove_class ("error");
Packit Service f2b131
            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
Packit Service f2b131
Packit Service f2b131
            return true;
Packit Service f2b131
        }
Packit Service f2b131
        catch (VariantParseError e)
Packit Service f2b131
        {
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (!context.has_class ("error"))
Packit Service f2b131
                context.add_class ("error");
Packit Service f2b131
            secondary_icon_name = "dialog-error-symbolic";
Packit Service f2b131
Packit Service f2b131
            return false;
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private void set_lock (bool state)
Packit Service f2b131
        requires (deleted_text_handler != 0 && inserted_text_handler != 0)
Packit Service f2b131
    {
Packit Service f2b131
        if (state)
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.block (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.block (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
        else
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.unblock (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.unblock (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        set_lock (true);
Packit Service f2b131
        this.text = gvariant.print (false);
Packit Service f2b131
        if (!test_value ())
Packit Service f2b131
            assert_not_reached ();
Packit Service f2b131
        set_lock (false);
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildNumberInt : SpinButton, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private string key_type;
Packit Service f2b131
Packit Service f2b131
    private ulong deleted_text_handler = 0;
Packit Service f2b131
    private ulong inserted_text_handler = 0;
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildNumberInt (Variant initial_value, string type_string, Variant? range_content_or_null)
Packit Service f2b131
        requires (type_string == "y" || type_string == "n" || type_string == "q" || type_string == "i" || type_string == "u" || type_string == "h")     // TODO type_string == "x" || type_string == "t" ||
Packit Service f2b131
    {
Packit Service f2b131
        this.key_type = type_string;
Packit Service f2b131
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.halign = Align.START;
Packit Service f2b131
Packit Service f2b131
        double min, max;
Packit Service f2b131
        if (range_content_or_null != null)
Packit Service f2b131
        {
Packit Service f2b131
            min = get_variant_as_double (((!) range_content_or_null).get_child_value (0));
Packit Service f2b131
            max = get_variant_as_double (((!) range_content_or_null).get_child_value (1));
Packit Service f2b131
        }
Packit Service f2b131
        else
Packit Service f2b131
            get_min_and_max_double (out min, out max, type_string);
Packit Service f2b131
Packit Service f2b131
        Adjustment adjustment = new Adjustment (get_variant_as_double (initial_value), min, max, 1.0, 5.0, 0.0);
Packit Service f2b131
        this.configure (adjustment, 1.0, 0);
Packit Service f2b131
Packit Service f2b131
        this.update_policy = SpinButtonUpdatePolicy.IF_VALID;
Packit Service f2b131
        this.snap_to_ticks = true;
Packit Service f2b131
        this.numeric = true;
Packit Service f2b131
        this.input_purpose = InputPurpose.NUMBER;   // TODO could be DIGITS for UnsignedInt
Packit Service f2b131
        this.width_chars = 30;
Packit Service f2b131
Packit Service f2b131
        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
Packit Service f2b131
        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed ());
Packit Service f2b131
        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed ());
Packit Service f2b131
        ulong entry_activate_handler = activate.connect (() => { update (); child_activated (); });
Packit Service f2b131
Packit Service f2b131
        destroy.connect (() => {
Packit Service f2b131
                ref_buffer.disconnect (deleted_text_handler);
Packit Service f2b131
                ref_buffer.disconnect (inserted_text_handler);
Packit Service f2b131
                disconnect (entry_activate_handler);
Packit Service f2b131
            });
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private static void get_min_and_max_double (out double min, out double max, string variant_type)
Packit Service f2b131
    {
Packit Service f2b131
        switch (variant_type)
Packit Service f2b131
        {
Packit Service f2b131
            case "y": min = (double) uint8.MIN;     max = (double) uint8.MAX;   break;
Packit Service f2b131
            case "n": min = (double) int16.MIN;     max = (double) int16.MAX;   break;
Packit Service f2b131
            case "q": min = (double) uint16.MIN;    max = (double) uint16.MAX;  break;
Packit Service f2b131
            case "i": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
Packit Service f2b131
            case "u": min = (double) uint32.MIN;    max = (double) uint32.MAX;  break;
Packit Service f2b131
            case "h": min = (double) int32.MIN;     max = (double) int32.MAX;   break;
Packit Service f2b131
            default: assert_not_reached ();
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private static double get_variant_as_double (Variant variant)
Packit Service f2b131
    {
Packit Service f2b131
        switch (variant.classify ())
Packit Service f2b131
        {
Packit Service f2b131
            case Variant.Class.BYTE:    return (double) variant.get_byte ();
Packit Service f2b131
            case Variant.Class.INT16:   return (double) variant.get_int16 ();
Packit Service f2b131
            case Variant.Class.UINT16:  return (double) variant.get_uint16 ();
Packit Service f2b131
            case Variant.Class.INT32:   return (double) variant.get_int32 ();
Packit Service f2b131
            case Variant.Class.UINT32:  return (double) variant.get_uint32 ();
Packit Service f2b131
            case Variant.Class.HANDLE:  return (double) variant.get_handle ();
Packit Service f2b131
            default: assert_not_reached ();
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()   // TODO test_value against range
Packit Service f2b131
    {
Packit Service f2b131
        switch (key_type)
Packit Service f2b131
        {
Packit Service f2b131
            case "y": return new Variant.byte   ((uchar)  get_int64_from_entry ()); // TODO uchar or uint8?
Packit Service f2b131
            case "n": return new Variant.int16  ((int16)  get_int64_from_entry ());
Packit Service f2b131
            case "q": return new Variant.uint16 ((uint16) get_int64_from_entry ());
Packit Service f2b131
            case "i": return new Variant.int32  ((int32)  get_int64_from_entry ());
Packit Service f2b131
            case "u": return new Variant.uint32 ((uint32) get_int64_from_entry ()); // TODO also use get_value_as_int?
Packit Service f2b131
            case "h": return new Variant.handle ((int32)  get_int64_from_entry ());
Packit Service f2b131
            default: assert_not_reached ();
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
    private int64 get_int64_from_entry ()
Packit Service f2b131
    {
Packit Service f2b131
        return int64.parse (this.get_text ());
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private void set_lock (bool state)
Packit Service f2b131
        requires (deleted_text_handler != 0 && inserted_text_handler != 0)
Packit Service f2b131
    {
Packit Service f2b131
        if (state)
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.block (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.block (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
        else
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.unblock (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.unblock (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)       // TODO "key_editor_child_number_int_real_reload: assertion 'gvariant != NULL' failed" two times when ghosting a key
Packit Service f2b131
    {
Packit Service f2b131
        set_lock (true);
Packit Service f2b131
        this.set_value (get_variant_as_double (gvariant));
Packit Service f2b131
        set_lock (false);
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildArray : Grid, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private TextView text_view;
Packit Service f2b131
    private Revealer error_revealer;
Packit Service f2b131
    private string key_type;
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
Packit Service f2b131
    private ulong deleted_text_handler = 0;
Packit Service f2b131
    private ulong inserted_text_handler = 0;
Packit Service f2b131
Packit Service f2b131
    construct
Packit Service f2b131
    {
Packit Service f2b131
        get_style_context ().add_class ("key-editor-child-array");
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildArray (string type_string, Variant initial_value)
Packit Service f2b131
    {
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.vexpand = false;
Packit Service f2b131
        orientation = Orientation.VERTICAL;
Packit Service f2b131
        get_style_context ().add_class ("frame");
Packit Service f2b131
Packit Service f2b131
        this.key_type = type_string;
Packit Service f2b131
        this.variant = initial_value;
Packit Service f2b131
Packit Service f2b131
        ScrolledWindow scrolled_window = new ScrolledWindow (null, null);
Packit Service f2b131
        scrolled_window.visible = true;
Packit Service f2b131
Packit Service f2b131
        text_view = new TextView ();
Packit Service f2b131
        text_view.visible = true;
Packit Service f2b131
        text_view.expand = true;
Packit Service f2b131
        text_view.wrap_mode = WrapMode.WORD;
Packit Service f2b131
        text_view.monospace = true;
Packit Service f2b131
        text_view.key_press_event.connect ((event) => {
Packit Service f2b131
                string keyval_name = (!) (Gdk.keyval_name (event.keyval) ?? "");
Packit Service f2b131
                if ((keyval_name == "Return" || keyval_name == "KP_Enter")
Packit Service f2b131
                && ((event.state & Gdk.ModifierType.MODIFIER_MASK) == 0)
Packit Service f2b131
                && (test_value ()))
Packit Service f2b131
                {
Packit Service f2b131
                    child_activated ();
Packit Service f2b131
                    return true;
Packit Service f2b131
                }
Packit Service f2b131
                return base.key_press_event (event);
Packit Service f2b131
            });
Packit Service f2b131
        // https://bugzilla.gnome.org/show_bug.cgi?id=789676
Packit Service f2b131
        text_view.button_press_event.connect_after (() => Gdk.EVENT_STOP);
Packit Service f2b131
        text_view.button_release_event.connect_after (() => Gdk.EVENT_STOP);
Packit Service f2b131
Packit Service f2b131
        scrolled_window.add (text_view);
Packit Service f2b131
        add (scrolled_window);
Packit Service f2b131
Packit Service f2b131
        error_revealer = new Revealer ();
Packit Service f2b131
        error_revealer.visible = true;
Packit Service f2b131
        error_revealer.transition_type = RevealerTransitionType.SLIDE_UP;
Packit Service f2b131
        error_revealer.reveal_child = false;
Packit Service f2b131
        add (error_revealer);
Packit Service f2b131
Packit Service f2b131
        ActionBar error_bar = new ActionBar ();
Packit Service f2b131
        error_bar.visible = true;
Packit Service f2b131
        error_revealer.add (error_bar);
Packit Service f2b131
Packit Service f2b131
        Image error_icon = new Image.from_icon_name ("dialog-error-symbolic", IconSize.BUTTON);
Packit Service f2b131
        error_icon.visible = true;
Packit Service f2b131
        error_bar.pack_start (error_icon);
Packit Service f2b131
Packit Service f2b131
        Label error_label = new Label (_("This value is invalid for the key type."));
Packit Service f2b131
        error_label.visible = true;
Packit Service f2b131
        error_bar.pack_start (error_label);
Packit Service f2b131
Packit Service f2b131
        text_view.buffer.text = initial_value.print (false);
Packit Service f2b131
Packit Service f2b131
        TextBuffer ref_buffer = text_view.buffer;    // an TextBuffer doesn't emit a "destroy" signal
Packit Service f2b131
        deleted_text_handler = ref_buffer.delete_range.connect_after (() => value_has_changed (test_value ()));
Packit Service f2b131
        inserted_text_handler = ref_buffer.insert_text.connect_after (() => value_has_changed (test_value ()));
Packit Service f2b131
        destroy.connect (() => {
Packit Service f2b131
                ref_buffer.disconnect (deleted_text_handler);
Packit Service f2b131
                ref_buffer.disconnect (inserted_text_handler);
Packit Service f2b131
            });
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private bool test_value ()
Packit Service f2b131
    {
Packit Service f2b131
        string tmp_text = text_view.buffer.text; // don't put in the try{} for correct C code
Packit Service f2b131
        try
Packit Service f2b131
        {
Packit Service f2b131
            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
Packit Service f2b131
            variant = (!) tmp_variant;
Packit Service f2b131
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (context.has_class ("error"))
Packit Service f2b131
                context.remove_class ("error");
Packit Service f2b131
            error_revealer.reveal_child = false;
Packit Service f2b131
Packit Service f2b131
            return true;
Packit Service f2b131
        }
Packit Service f2b131
        catch (VariantParseError e)
Packit Service f2b131
        {
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (!context.has_class ("error"))
Packit Service f2b131
                context.add_class ("error");
Packit Service f2b131
            error_revealer.reveal_child = true;
Packit Service f2b131
Packit Service f2b131
            return false;
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private void set_lock (bool state)
Packit Service f2b131
        requires (deleted_text_handler != 0 && inserted_text_handler != 0)
Packit Service f2b131
    {
Packit Service f2b131
        if (state)
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.block (text_view.buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.block (text_view.buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
        else
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.unblock (text_view.buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.unblock (text_view.buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        set_lock (true);
Packit Service f2b131
        text_view.buffer.text = gvariant.print (false);
Packit Service f2b131
        if (!test_value ())
Packit Service f2b131
            assert_not_reached ();
Packit Service f2b131
        set_lock (false);
Packit Service f2b131
    }
Packit Service f2b131
}
Packit Service f2b131
Packit Service f2b131
private class KeyEditorChildDefault : Entry, KeyEditorChild
Packit Service f2b131
{
Packit Service f2b131
    private string key_type;
Packit Service f2b131
    private Variant variant;
Packit Service f2b131
    private bool is_string;
Packit Service f2b131
Packit Service f2b131
    private ulong deleted_text_handler = 0;
Packit Service f2b131
    private ulong inserted_text_handler = 0;
Packit Service f2b131
Packit Service f2b131
    construct
Packit Service f2b131
    {
Packit Service f2b131
        get_style_context ().add_class ("key-editor-child-entry");
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public KeyEditorChildDefault (string type_string, Variant initial_value)
Packit Service f2b131
    {
Packit Service f2b131
        this.key_type = type_string;
Packit Service f2b131
        this.variant = initial_value;
Packit Service f2b131
Packit Service f2b131
        this.visible = true;
Packit Service f2b131
        this.hexpand = true;
Packit Service f2b131
        this.secondary_icon_activatable = false;
Packit Service f2b131
        this.set_icon_tooltip_text (EntryIconPosition.SECONDARY, _("This value is invalid for the key type."));
Packit Service f2b131
Packit Service f2b131
        this.is_string = type_string == "s" || type_string == "o" || type_string == "g";
Packit Service f2b131
        this.text = is_string ? initial_value.get_string () : initial_value.print (false);
Packit Service f2b131
Packit Service f2b131
        EntryBuffer ref_buffer = buffer;    // an EntryBuffer doesn't emit a "destroy" signal
Packit Service f2b131
        deleted_text_handler = ref_buffer.deleted_text.connect (() => value_has_changed (test_value ()));
Packit Service f2b131
        inserted_text_handler = ref_buffer.inserted_text.connect (() => value_has_changed (test_value ()));
Packit Service f2b131
        ulong entry_activate_handler = activate.connect (() => { if (test_value ()) child_activated (); });
Packit Service f2b131
Packit Service f2b131
        destroy.connect (() => {
Packit Service f2b131
                ref_buffer.disconnect (deleted_text_handler);
Packit Service f2b131
                ref_buffer.disconnect (inserted_text_handler);
Packit Service f2b131
                disconnect (entry_activate_handler);
Packit Service f2b131
            });
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private bool test_value ()
Packit Service f2b131
    {
Packit Service f2b131
        if (key_type == "s")
Packit Service f2b131
        {
Packit Service f2b131
            variant = new Variant.string (this.text);
Packit Service f2b131
            return true;
Packit Service f2b131
        }
Packit Service f2b131
Packit Service f2b131
        string tmp_text = is_string ? @"'$text'" : this.text; // don't put in the try{} for correct C code
Packit Service f2b131
        try
Packit Service f2b131
        {
Packit Service f2b131
            Variant? tmp_variant = Variant.parse (new VariantType (key_type), tmp_text);
Packit Service f2b131
            variant = (!) tmp_variant;
Packit Service f2b131
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (context.has_class ("error"))
Packit Service f2b131
                context.remove_class ("error");
Packit Service f2b131
            set_icon_from_icon_name (EntryIconPosition.SECONDARY, null);
Packit Service f2b131
Packit Service f2b131
            return true;
Packit Service f2b131
        }
Packit Service f2b131
        catch (VariantParseError e)
Packit Service f2b131
        {
Packit Service f2b131
            StyleContext context = get_style_context ();
Packit Service f2b131
            if (!context.has_class ("error"))
Packit Service f2b131
                context.add_class ("error");
Packit Service f2b131
            secondary_icon_name = "dialog-error-symbolic";
Packit Service f2b131
Packit Service f2b131
            return false;
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public Variant get_variant ()
Packit Service f2b131
    {
Packit Service f2b131
        return variant;
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    private void set_lock (bool state)
Packit Service f2b131
        requires (deleted_text_handler != 0 && inserted_text_handler != 0)
Packit Service f2b131
    {
Packit Service f2b131
        if (state)
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.block (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.block (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
        else
Packit Service f2b131
        {
Packit Service f2b131
            SignalHandler.unblock (buffer, deleted_text_handler);
Packit Service f2b131
            SignalHandler.unblock (buffer, inserted_text_handler);
Packit Service f2b131
        }
Packit Service f2b131
    }
Packit Service f2b131
Packit Service f2b131
    public void reload (Variant gvariant)
Packit Service f2b131
    {
Packit Service f2b131
        set_lock (true);
Packit Service f2b131
        this.text = is_string ? gvariant.get_string () : gvariant.print (false);
Packit Service f2b131
        if (!test_value ())
Packit Service f2b131
            assert_not_reached ();
Packit Service f2b131
        set_lock (false);
Packit Service f2b131
    }
Packit Service f2b131
}