diff --git a/NEWS b/NEWS index 77fd1ec814..48f497c6bd 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE! * The "band" property of Wi-fi connections now accepts the "6GHz" value. * Show the Wi-Fi band of APs in the scan results from nmcli. +* New button in nmtui that allows users to chose from list of + available devices when creating connection profiles for physical interfaces + (Ethernet, Wi-Fi, etc.). ============================================= NetworkManager-1.56 diff --git a/src/nmtui/nmt-device-entry.c b/src/nmtui/nmt-device-entry.c index 99cc43add3..2034aabd16 100644 --- a/src/nmtui/nmt-device-entry.c +++ b/src/nmtui/nmt-device-entry.c @@ -14,11 +14,6 @@ * the entry recognizes the interface name or mac address typed in as * matching a known #NMDevice, then it will also display the other * property in parentheses. - * - * FIXME: #NmtDeviceEntry is currently an #NmtEditorGrid object, so that - * we can possibly eventually add a button to its "extra" field, that - * would pop up a form for selecting a device. But if we're not going - * to implement that then we should make it just an #NmtNewtEntry. */ #include "libnm-client-aux-extern/nm-default-client.h" @@ -49,6 +44,7 @@ typedef struct { NmtNewtWidget *button; gboolean updating; + gboolean show_select_button; } NmtDeviceEntryPrivate; enum { @@ -58,6 +54,7 @@ enum { PROP_HARDWARE_TYPE, PROP_INTERFACE_NAME, PROP_MAC_ADDRESS, + PROP_SHOW_SELECT_BUTTON, LAST_PROP }; @@ -68,16 +65,18 @@ enum { * @width: the width of the entry * @hardware_type: the type of #NMDevice to be selected, or * %G_TYPE_NONE if this is for a virtual device type. + * @show_select_button: whether to show select button or not. * * Creates a new #NmtDeviceEntry, for identifying a device of type * @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not * set a #NmtDeviceEntryDeviceFilter), then this will only allow - * specifying an interface name, not a hardware address. + * specifying an interface name, not a hardware address. @show_select_button + * will allow the user to select from a list of available devices of type @hardware_type. * * Returns: a new #NmtDeviceEntry. */ NmtNewtWidget * -nmt_device_entry_new(const char *label, int width, GType hardware_type) +nmt_device_entry_new(const char *label, int width, GType hardware_type, gboolean show_select_button) { return g_object_new(NMT_TYPE_DEVICE_ENTRY, "label", @@ -86,6 +85,8 @@ nmt_device_entry_new(const char *label, int width, GType hardware_type) width, "hardware-type", hardware_type, + "show-select-button", + show_select_button, NULL); } @@ -333,6 +334,133 @@ entry_text_changed(GObject *object, GParamSpec *pspec, gpointer deventry) g_free(mac); } +static void +device_selected(NmtNewtWidget *listbox, gpointer user_data) +{ + NMDevice *candidate = nmt_newt_listbox_get_active_key(NMT_NEWT_LISTBOX(listbox)); + NmtDeviceEntry *deventry = NMT_DEVICE_ENTRY(user_data); + const char *ifname; + + if (!candidate) + return; + + ifname = nm_device_get_iface(candidate); + if (!ifname) + return; + + if (nmt_device_entry_set_interface_name(deventry, ifname)) + update_entry(deventry); +} + +static int +compare_devices_by_name(gconstpointer a, gconstpointer b) +{ + NMDevice **dev_a = (NMDevice **) a; + NMDevice **dev_b = (NMDevice **) b; + + return nm_strcmp0(nm_device_get_iface(*dev_a), nm_device_get_iface(*dev_b)); +} + +static void +do_select_dialog(NmtNewtWidget *button, gpointer user_data) +{ + NmtDeviceEntry *deventry; + NmtDeviceEntryPrivate *priv; + gs_unref_object NmtNewtForm *popup_form = NULL; + NmtNewtWidget *listbox_widget; + NmtNewtForm *parent_form; + const GPtrArray *devices; + gs_unref_ptrarray GPtrArray *matching_devices = NULL; + const char *ifname, *driver; + int i; + int entry_x, entry_y; + int window_x, window_y; + int popup_x, popup_y; + int list_w, list_h; + newtComponent entry_component; + + deventry = NMT_DEVICE_ENTRY(user_data); + priv = NMT_DEVICE_ENTRY_GET_PRIVATE(deventry); + parent_form = nmt_newt_widget_get_form(NMT_NEWT_WIDGET(deventry)); + if (!parent_form) + return; + + matching_devices = g_ptr_array_new(); + + entry_component = nmt_newt_component_get_component(NMT_NEWT_COMPONENT(priv->entry)); + newtComponentGetPosition(entry_component, &entry_x, &entry_y); + g_object_get(parent_form, "x", &window_x, "y", &window_y, NULL); + + listbox_widget = nmt_newt_listbox_new(5, NMT_NEWT_LISTBOX_SCROLL); + nmt_newt_widget_set_exit_on_activate(listbox_widget, TRUE); + + nmt_newt_widget_set_padding(listbox_widget, 1, 0, 1, 0); + + devices = nm_client_get_devices(nm_client); + for (i = 0; i < devices->len; i++) { + NMDevice *candidate = devices->pdata[i]; + + if (!G_TYPE_CHECK_INSTANCE_TYPE(candidate, priv->hardware_type)) + continue; + + if (priv->device_filter + && !priv->device_filter(deventry, candidate, priv->device_filter_data)) + continue; + + ifname = nm_device_get_iface(candidate); + if (!ifname) + continue; + + g_ptr_array_add(matching_devices, candidate); + } + + if (matching_devices->len == 0) { + nmt_newt_message_dialog(_("No devices available")); + return; + } + + g_ptr_array_sort(matching_devices, compare_devices_by_name); + + for (i = 0; i < matching_devices->len; i++) { + gs_free char *display_text = NULL; + NMDevice *candidate = matching_devices->pdata[i]; + + ifname = nm_device_get_iface(candidate); + + driver = nm_device_get_driver(candidate); + + if (driver && driver[0] != '\0') { + display_text = g_strdup_printf("%s (%s)", ifname, driver); + } else { + display_text = g_strdup(ifname); + } + + nmt_newt_listbox_append(NMT_NEWT_LISTBOX(listbox_widget), display_text, candidate); + } + + g_signal_connect(listbox_widget, "activated", G_CALLBACK(device_selected), deventry); + + nmt_newt_widget_size_request(listbox_widget, &list_w, &list_h); + popup_x = window_x + entry_x + 1; + popup_y = window_y + entry_y + 1; + + popup_form = g_object_new(NMT_TYPE_NEWT_FORM, + "x", + popup_x, + "y", + popup_y, + "width", + list_w, + "height", + list_h, + "padding", + 0, + NULL); + + nmt_newt_form_set_content(popup_form, listbox_widget); + nmt_newt_form_show(popup_form); +} + static void nmt_device_entry_init(NmtDeviceEntry *deventry) { @@ -346,11 +474,9 @@ nmt_device_entry_init(NmtDeviceEntry *deventry) nmt_newt_entry_set_validator(priv->entry, device_entry_validate, deventry); g_signal_connect(priv->entry, "notify::text", G_CALLBACK(entry_text_changed), deventry); -#if 0 - priv->button = nmt_newt_button_new (_("Select...")); - g_signal_connect (priv->button, "clicked", - G_CALLBACK (do_select_dialog), deventry); -#endif + priv->button = nmt_newt_button_new(_("Select...")); + nmt_newt_widget_set_visible(priv->button, FALSE); + g_signal_connect(priv->button, "clicked", G_CALLBACK(do_select_dialog), deventry); } static void @@ -361,7 +487,7 @@ nmt_device_entry_constructed(GObject *object) nmt_editor_grid_append(NMT_EDITOR_GRID(object), priv->label, NMT_NEWT_WIDGET(priv->entry), - NULL); + priv->button); G_OBJECT_CLASS(nmt_device_entry_parent_class)->constructed(object); } @@ -446,6 +572,10 @@ nmt_device_entry_set_property(GObject *object, if (nmt_device_entry_set_mac_address(deventry, mac_address)) update_entry(deventry); break; + case PROP_SHOW_SELECT_BUTTON: + priv->show_select_button = g_value_get_boolean(value); + nmt_newt_widget_set_visible(priv->button, priv->show_select_button); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -473,6 +603,9 @@ nmt_device_entry_get_property(GObject *object, guint prop_id, GValue *value, GPa case PROP_MAC_ADDRESS: g_value_set_string(value, priv->mac_address); break; + case PROP_SHOW_SELECT_BUTTON: + g_value_set_boolean(value, priv->show_select_button); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -554,4 +687,18 @@ nmt_device_entry_class_init(NmtDeviceEntryClass *deventry_class) "", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * NmtDeviceEntry:show-select-button: + * + * Display select button to select device from available devices. + */ + g_object_class_install_property( + object_class, + PROP_SHOW_SELECT_BUTTON, + g_param_spec_boolean("show-select-button", + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } diff --git a/src/nmtui/nmt-device-entry.h b/src/nmtui/nmt-device-entry.h index 6265ce019f..21be28c762 100644 --- a/src/nmtui/nmt-device-entry.h +++ b/src/nmtui/nmt-device-entry.h @@ -30,7 +30,10 @@ typedef struct { GType nmt_device_entry_get_type(void); -NmtNewtWidget *nmt_device_entry_new(const char *label, int width, GType hardware_type); +NmtNewtWidget *nmt_device_entry_new(const char *label, + int width, + GType hardware_type, + gboolean show_select_button); typedef gboolean (*NmtDeviceEntryDeviceFilter)(NmtDeviceEntry *deventry, NMDevice *device, diff --git a/src/nmtui/nmt-editor-grid.c b/src/nmtui/nmt-editor-grid.c index bdcc02e259..21d362c439 100644 --- a/src/nmtui/nmt-editor-grid.c +++ b/src/nmtui/nmt-editor-grid.c @@ -244,8 +244,10 @@ nmt_editor_grid_get_components(NmtNewtWidget *widget) if (rows[i].extra) { child_cos = nmt_newt_widget_get_components(rows[i].extra); - for (c = 0; child_cos[c]; c++) - g_ptr_array_add(cos, child_cos[c]); + if (child_cos) { + for (c = 0; child_cos[c]; c++) + g_ptr_array_add(cos, child_cos[c]); + } g_free(child_cos); } } diff --git a/src/nmtui/nmt-editor.c b/src/nmtui/nmt-editor.c index 474d08c278..f1dd43860d 100644 --- a/src/nmtui/nmt-editor.c +++ b/src/nmtui/nmt-editor.c @@ -310,6 +310,7 @@ nmt_editor_constructed(GObject *object) GType hardware_type; const char *port_type; NmtEditorPage *page; + gboolean show_select_button; if (G_OBJECT_CLASS(nmt_editor_parent_class)->constructed) G_OBJECT_CLASS(nmt_editor_parent_class)->constructed(object); @@ -333,10 +334,13 @@ nmt_editor_constructed(GObject *object) G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); nmt_editor_grid_append(grid, _("Profile name"), widget, NULL); - if (priv->type_data->virtual) - hardware_type = G_TYPE_NONE; - else - hardware_type = priv->type_data->device_type; + if (priv->type_data->virtual) { + hardware_type = G_TYPE_NONE; + show_select_button = FALSE; + } else { + hardware_type = priv->type_data->device_type; + show_select_button = TRUE; + } if (nm_connection_is_type(priv->edit_connection, NM_SETTING_LOOPBACK_SETTING_NAME)) { g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "lo", NULL); @@ -349,7 +353,7 @@ nmt_editor_constructed(GObject *object) else deventry_label = _("Device"); - widget = nmt_device_entry_new(deventry_label, 40, hardware_type); + widget = nmt_device_entry_new(deventry_label, 40, hardware_type, show_select_button); nmt_editor_grid_append(grid, NULL, widget, NULL); deventry = NMT_DEVICE_ENTRY(widget); g_object_bind_property(s_con, diff --git a/src/nmtui/nmt-page-ip-tunnel.c b/src/nmtui/nmt-page-ip-tunnel.c index c74037b628..4932c0dc9f 100644 --- a/src/nmtui/nmt-page-ip-tunnel.c +++ b/src/nmtui/nmt-page-ip-tunnel.c @@ -127,7 +127,7 @@ nmt_page_ip_tunnel_constructed(GObject *object) w2s); nmt_editor_grid_append(grid, _("Mode"), widget, NULL); - widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE); + widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE, FALSE); g_object_bind_property(s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, widget, diff --git a/src/nmtui/nmt-page-macsec.c b/src/nmtui/nmt-page-macsec.c index 6a0174868c..cd6a54941e 100644 --- a/src/nmtui/nmt-page-macsec.c +++ b/src/nmtui/nmt-page-macsec.c @@ -99,7 +99,7 @@ nmt_page_macsec_constructed(GObject *object) section = nmt_editor_section_new(_("MACsec"), NULL, TRUE); grid = nmt_editor_section_get_body(section); - widget = nmt_device_entry_new(_("Parent device"), 40, G_TYPE_NONE); + widget = nmt_device_entry_new(_("Parent device"), 40, G_TYPE_NONE, FALSE); g_object_bind_property(s_macsec, NM_SETTING_MACSEC_PARENT, widget, diff --git a/src/nmtui/nmt-page-vlan.c b/src/nmtui/nmt-page-vlan.c index 238dbafb5b..64c10ef760 100644 --- a/src/nmtui/nmt-page-vlan.c +++ b/src/nmtui/nmt-page-vlan.c @@ -59,7 +59,7 @@ nmt_page_vlan_constructed(GObject *object) nm_editor_bind_vlan_name(s_vlan, nm_connection_get_setting_connection(conn)); - widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE); + widget = parent = nmt_device_entry_new(_("Parent"), 40, G_TYPE_NONE, FALSE); nmt_device_entry_set_device_filter(NMT_DEVICE_ENTRY(widget), vlan_device_filter, vlan); g_object_bind_property(s_vlan, NM_SETTING_VLAN_PARENT,