/* * Copyright (c) 2012-2025 Daniele Bartolini et al. * SPDX-License-Identifier: GPL-3.0-or-later */ namespace Crown { public class InputEnum : InputField, Gtk.ComboBox { public bool _inconsistent; public Gtk.ListStore _store; public Gtk.TreeModelFilter _filter; public Gtk.EventControllerScroll _controller_scroll; public void set_inconsistent(bool inconsistent) { if (_inconsistent != inconsistent) { _inconsistent = inconsistent; _filter.refilter(); if (_inconsistent) { this.set_active_id(INCONSISTENT_ID); } } } public bool is_inconsistent() { return _inconsistent; } public virtual GLib.Value union_value() { return this.value; } public virtual void set_union_value(GLib.Value v) { this.value = (string)v; } public string value { get { return this.get_active_id(); } set { _filter.refilter(); bool success = this.set_active_id(value); set_inconsistent(!success); } } public bool filter_visible_func(Gtk.TreeModel model, Gtk.TreeIter iter) { Value id_val; model.get_value(iter, 0, out id_val); if (!_inconsistent && (string)id_val == INCONSISTENT_ID) return false; return true; } public InputEnum(string default_id = "DEFAULT", string[]? labels = null, string[]? ids = null) { _inconsistent = false; _store = new Gtk.ListStore(2 , typeof(string) // ID , typeof(string) // Label ); _filter = new Gtk.TreeModelFilter(_store, null); _filter.set_visible_func(filter_visible_func); this.model = _filter; this.id_column = 0; this.entry_text_column = 1; Gtk.CellRendererText renderer = new Gtk.CellRendererText(); this.pack_start(renderer, true); this.add_attribute(renderer, "text", 1); insert_special_values(); if (labels != null) { Gtk.TreeIter iter; for (int ii = 0; ii < ids.length; ++ii) { unowned string? id = ids != null ? ids[ii] : null; _store.insert_with_values(out iter, -1, 0, id, 1, labels[ii], -1); } // if (ids != null && default_id < ids.length) this.value = default_id; } this.changed.connect(on_changed); #if CROWN_GTK3 this.scroll_event.connect(() => { GLib.Signal.stop_emission_by_name(this, "scroll-event"); return Gdk.EVENT_PROPAGATE; }); #else _controller_scroll = new Gtk.EventControllerScroll(this, Gtk.EventControllerScrollFlags.BOTH_AXES); _controller_scroll.set_propagation_phase(Gtk.PropagationPhase.CAPTURE); _controller_scroll.scroll.connect(() => { // Do nothing, just consume the event to stop // the annoying scroll default behavior. }); #endif } public void append(string? id, string label) { this.changed.disconnect(on_changed); Gtk.TreeIter iter; _store.insert_with_values(out iter, -1, 0, id, 1, label, -1); this.changed.connect(on_changed); } public void clear() { this.changed.disconnect(on_changed); _store.clear(); insert_special_values(); _inconsistent = false; this.changed.connect(on_changed); } public string any_valid_id() { string some_id = INCONSISTENT_ID; if (_store.iter_n_children(null) > 1u) { _store.foreach((model, path, iter) => { Value id_val; model.get_value(iter, 0, out id_val); if ((string)id_val != INCONSISTENT_ID) { some_id = (string)id_val; return true; } return false; }); } return some_id; } public void on_changed() { if (this.get_active_id() == null) return; if (_inconsistent && this.get_active_id() == INCONSISTENT_ID) return; if (_inconsistent) { _inconsistent = false; _filter.refilter(); } value_changed(this); } public void insert_special_values() { assert(_store.iter_n_children(null) == 0u); // Insert the "inconsistent" ID and label. Gtk.TreeIter iter; _store.insert_with_values(out iter, -1, 0, INCONSISTENT_ID, 1, INCONSISTENT_LABEL, -1); } } } /* namespace Crown */