Get basic new account verification working.
This commit is contained in:
parent
bb629999a6
commit
468a7a674b
4 changed files with 259 additions and 112 deletions
|
|
@ -50,15 +50,20 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
[GtkChild]
|
||||
private Gtk.Button create_button;
|
||||
|
||||
[GtkChild]
|
||||
private Gtk.Spinner create_spinner;
|
||||
|
||||
private NameRow real_name;
|
||||
private EmailRow email = new EmailRow();
|
||||
private string last_valid_email = "";
|
||||
|
||||
private HostnameRow imap_hostname = new HostnameRow(Geary.Protocol.IMAP);
|
||||
private TransportSecurityRow imap_tls = new TransportSecurityRow();
|
||||
private LoginRow imap_login = new LoginRow();
|
||||
private PasswordRow imap_password = new PasswordRow();
|
||||
|
||||
private HostnameRow smtp_hostname = new HostnameRow(Geary.Protocol.SMTP);
|
||||
private TransportSecurityRow smtp_tls = new TransportSecurityRow();
|
||||
private SmtpAuthRow smtp_auth = new SmtpAuthRow();
|
||||
private LoginRow smtp_login = new LoginRow();
|
||||
private PasswordRow smtp_password = new PasswordRow();
|
||||
|
|
@ -101,20 +106,24 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
this.email.value.changed.connect(on_email_changed);
|
||||
|
||||
this.imap_hostname.validator.notify["state"].connect(on_validated);
|
||||
this.imap_tls.hide();
|
||||
this.imap_login.validator.notify["state"].connect(on_validated);
|
||||
this.imap_password.validator.notify["state"].connect(on_validated);
|
||||
|
||||
this.smtp_hostname.validator.notify["state"].connect(on_validated);
|
||||
this.smtp_tls.hide();
|
||||
this.smtp_auth.value.changed.connect(on_smtp_auth_changed);
|
||||
this.smtp_login.validator.notify["state"].connect(on_validated);
|
||||
this.smtp_password.validator.notify["state"].connect(on_validated);
|
||||
|
||||
if (provider == Geary.ServiceProvider.OTHER) {
|
||||
this.receiving_list.add(this.imap_hostname);
|
||||
this.receiving_list.add(this.imap_tls);
|
||||
this.receiving_list.add(this.imap_login);
|
||||
this.receiving_list.add(this.imap_password);
|
||||
|
||||
this.sending_list.add(this.smtp_hostname);
|
||||
this.sending_list.add(this.smtp_tls);
|
||||
this.sending_list.add(this.smtp_auth);
|
||||
} else {
|
||||
this.details_list.add(this.imap_password);
|
||||
|
|
@ -139,10 +148,15 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
}
|
||||
|
||||
private async void validate_account(GLib.Cancellable? cancellable) {
|
||||
this.create_spinner.show();
|
||||
this.create_spinner.start();
|
||||
this.create_button.set_sensitive(false);
|
||||
this.set_sensitive(false);
|
||||
|
||||
bool is_valid = false;
|
||||
string message = "";
|
||||
Gtk.Widget? to_focus = null;
|
||||
|
||||
Geary.ServiceInformation imap = new_imap_service();
|
||||
Geary.ServiceInformation smtp = new_smtp_service();
|
||||
|
||||
|
|
@ -162,55 +176,92 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
try {
|
||||
yield this.engine.validate_imap(account, cancellable);
|
||||
imap_valid = true;
|
||||
} catch (Geary.ImapError.UNAUTHENTICATED err) {
|
||||
debug("Error authenticating IMAP service: %s", err.message);
|
||||
to_focus = this.imap_login.value;
|
||||
// Translators: In-app notification label
|
||||
message = _("Check your receiving login and password");
|
||||
} catch (GLib.Error err) {
|
||||
debug("Error validating IMAP service: %s", err.message);
|
||||
// XXX do something with this
|
||||
this.imap_tls.show();
|
||||
to_focus = this.imap_hostname.value;
|
||||
// Translators: In-app notification label
|
||||
message = _("Check your receiving server details");
|
||||
}
|
||||
|
||||
// Only validate SMTP if not using IMAP creds, or if the
|
||||
// IMAP creds are good, so we don't check known bad creds
|
||||
if (!smtp.smtp_use_imap_credentials || imap_valid) {
|
||||
//notification.label = _("Checking sending server…");
|
||||
|
||||
if (imap_valid) {
|
||||
debug("Validating SMTP...");
|
||||
try {
|
||||
yield this.engine.validate_smtp(account, cancellable);
|
||||
smtp_valid = true;
|
||||
} catch (Geary.SmtpError.AUTHENTICATION_FAILED err) {
|
||||
debug("Error authenticating SMTP service: %s", err.message);
|
||||
// There was an SMTP auth error, but IMAP already
|
||||
// succeeded, so the user probably needs to
|
||||
// specify custom creds here
|
||||
this.smtp_auth.source = Geary.SmtpCredentials.CUSTOM;
|
||||
to_focus = this.smtp_login.value;
|
||||
// Translators: In-app notification label
|
||||
message = _("Check your sending login and password");
|
||||
} catch (GLib.Error err) {
|
||||
debug("Error validating SMTP service: %s", err.message);
|
||||
// XXX do something with this
|
||||
this.smtp_tls.show();
|
||||
to_focus = this.smtp_hostname.value;
|
||||
// Translators: In-app notification label
|
||||
message = _("Check your sending server details");
|
||||
}
|
||||
}
|
||||
|
||||
is_valid = imap_valid && smtp_valid;
|
||||
} else {
|
||||
//notification.label = _("Checking account…");
|
||||
try {
|
||||
yield this.engine.validate_imap(account, cancellable);
|
||||
is_valid = true;
|
||||
} catch (Geary.ImapError.UNAUTHENTICATED err) {
|
||||
debug("Error authenticating provider: %s", err.message);
|
||||
to_focus = this.email.value;
|
||||
// Translators: In-app notification label
|
||||
message = _("Check your email address and password");
|
||||
} catch (GLib.Error err) {
|
||||
debug("Error validating provider IMAP: %s", err.message);
|
||||
// XXX do something with this
|
||||
debug("Error validating provider service: %s", err.message);
|
||||
is_valid = false;
|
||||
// Translators: In-app notification label
|
||||
message = _("Could not connect, check your network");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_valid) {
|
||||
try {
|
||||
yield this.accounts.create_account(account, cancellable);
|
||||
this.editor.pop();
|
||||
} catch (GLib.Error err) {
|
||||
debug("Failed to create new local account: %s", err.message);
|
||||
// XXX do something with this
|
||||
is_valid = false;
|
||||
// Translators: In-app notification label for a
|
||||
// generic error creating an account
|
||||
message = _("An unexpected problem occurred");
|
||||
}
|
||||
}
|
||||
|
||||
this.create_spinner.stop();
|
||||
this.create_spinner.hide();
|
||||
this.create_button.set_sensitive(true);
|
||||
this.set_sensitive(true);
|
||||
|
||||
// Focus and pop up the notification after re-sensitising
|
||||
// so it actually succeeds.
|
||||
if (!is_valid) {
|
||||
if (to_focus != null) {
|
||||
to_focus.grab_focus();
|
||||
}
|
||||
this.editor.pop();
|
||||
} else {
|
||||
add_notification(
|
||||
new InAppNotification(
|
||||
_("Account not added, check the details below")
|
||||
// Translators: In-app notification label, the
|
||||
// string substitution is a more detailed reason.
|
||||
_("Account not created: %s").printf(message)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.create_button.set_sensitive(true);
|
||||
this.set_sensitive(true);
|
||||
}
|
||||
|
||||
private LocalServiceInformation new_imap_service() {
|
||||
|
|
@ -230,6 +281,10 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
GLib.NetworkAddress address = host.validated_address;
|
||||
service.host = address.hostname;
|
||||
service.port = (uint16) address.port;
|
||||
|
||||
Geary.TlsNegotiationMethod tls = this.imap_tls.value.method;
|
||||
service.use_ssl = (tls == Geary.TlsNegotiationMethod.TRANSPORT);
|
||||
service.use_starttls = (tls == Geary.TlsNegotiationMethod.START_TLS);
|
||||
} else {
|
||||
this.provider.setup_service(service);
|
||||
service.credentials = new Geary.Credentials(
|
||||
|
|
@ -247,7 +302,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
this.accounts.new_libsecret_service(Geary.Protocol.SMTP);
|
||||
|
||||
if (this.provider == Geary.ServiceProvider.OTHER) {
|
||||
switch (this.smtp_auth.get_value()) {
|
||||
switch (this.smtp_auth.source) {
|
||||
case Geary.SmtpCredentials.NONE:
|
||||
service.smtp_noauth = true;
|
||||
service.smtp_use_imap_credentials = false;
|
||||
|
|
@ -276,6 +331,13 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
|
||||
service.host = address.hostname;
|
||||
service.port = (uint16) address.port;
|
||||
|
||||
Geary.TlsNegotiationMethod tls = this.smtp_tls.value.method;
|
||||
service.use_ssl = (tls == Geary.TlsNegotiationMethod.TRANSPORT);
|
||||
service.use_starttls = (tls == Geary.TlsNegotiationMethod.START_TLS);
|
||||
|
||||
debug("SMTP service: TLS: %s, STARTTLS: %s",
|
||||
service.use_ssl.to_string(), service.use_starttls.to_string());
|
||||
} else {
|
||||
this.provider.setup_service(service);
|
||||
}
|
||||
|
|
@ -319,7 +381,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
}
|
||||
|
||||
private void on_smtp_auth_changed() {
|
||||
if (this.smtp_auth.get_value() == Geary.SmtpCredentials.CUSTOM) {
|
||||
if (this.smtp_auth.source == Geary.SmtpCredentials.CUSTOM) {
|
||||
this.sending_list.add(this.smtp_login);
|
||||
this.sending_list.add(this.smtp_password);
|
||||
} else if (this.smtp_login.parent != null) {
|
||||
|
|
@ -346,6 +408,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
|
|||
Gtk.Container? next = null;
|
||||
if (direction == Gtk.DirectionType.DOWN) {
|
||||
if (widget == this.details_list) {
|
||||
debug("Have details!");
|
||||
next = this.receiving_list;
|
||||
} else if (widget == this.receiving_list) {
|
||||
next = this.sending_list;
|
||||
|
|
@ -512,9 +575,35 @@ private class Accounts.HostnameRow : EntryRow {
|
|||
}
|
||||
|
||||
|
||||
private class Accounts.TransportSecurityRow :
|
||||
LabelledEditorRow<EditorAddPane,TlsComboBox> {
|
||||
|
||||
public TransportSecurityRow() {
|
||||
TlsComboBox value = new TlsComboBox();
|
||||
base(value.label, value);
|
||||
// Set to Transport TLS by default per RFC 8314
|
||||
this.value.method = Geary.TlsNegotiationMethod.TRANSPORT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class Accounts.SmtpAuthRow :
|
||||
LabelledEditorRow<EditorAddPane,Gtk.ComboBoxText> {
|
||||
|
||||
public Geary.SmtpCredentials source {
|
||||
get {
|
||||
try {
|
||||
return Geary.SmtpCredentials.for_value(this.value.active_id);
|
||||
} catch {
|
||||
return Geary.SmtpCredentials.IMAP;
|
||||
}
|
||||
}
|
||||
set {
|
||||
this.value.active_id = value.to_value();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public SmtpAuthRow() {
|
||||
base(
|
||||
|
|
@ -530,15 +619,7 @@ private class Accounts.SmtpAuthRow :
|
|||
this.value.append(Geary.SmtpCredentials.IMAP.to_value(), _("Use IMAP login"));
|
||||
this.value.append(Geary.SmtpCredentials.CUSTOM.to_value(), _("Use different login"));
|
||||
|
||||
this.value.active_id = Geary.SmtpCredentials.IMAP.to_value();
|
||||
}
|
||||
|
||||
public Geary.SmtpCredentials get_value() {
|
||||
try {
|
||||
return Geary.SmtpCredentials.for_value(this.value.active_id);
|
||||
} catch {
|
||||
return Geary.SmtpCredentials.IMAP;
|
||||
}
|
||||
this.source = Geary.SmtpCredentials.IMAP;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,16 @@ public class GoaServiceInformation : Geary.ServiceInformation {
|
|||
if (mail != null) {
|
||||
switch (this.protocol) {
|
||||
case Geary.Protocol.IMAP:
|
||||
this.host = mail.imap_host;
|
||||
this.port = Geary.Imap.ClientConnection.IMAP_TLS_PORT;
|
||||
parse_host_name(mail.imap_host);
|
||||
this.use_ssl = mail.imap_use_ssl;
|
||||
this.use_starttls = mail.imap_use_tls;
|
||||
|
||||
if (this.port == 0) {
|
||||
this.port = this.use_ssl
|
||||
? Geary.Imap.ClientConnection.IMAP_TLS_PORT
|
||||
: Geary.Imap.ClientConnection.IMAP_PORT;
|
||||
}
|
||||
|
||||
this.credentials = new Geary.Credentials(
|
||||
((GoaMediator) this.mediator).method,
|
||||
mail.imap_user_name
|
||||
|
|
@ -36,12 +42,22 @@ public class GoaServiceInformation : Geary.ServiceInformation {
|
|||
break;
|
||||
|
||||
case Geary.Protocol.SMTP:
|
||||
this.host = mail.smtp_host;
|
||||
this.port = Geary.Smtp.ClientConnection.SUBMISSION_TLS_PORT;
|
||||
parse_host_name(mail.smtp_host);
|
||||
this.use_ssl = mail.smtp_use_ssl;
|
||||
this.use_starttls = mail.smtp_use_tls;
|
||||
this.smtp_noauth = !(mail.smtp_use_auth);
|
||||
this.smtp_use_imap_credentials = false;
|
||||
|
||||
if (this.port == 0) {
|
||||
if (this.use_ssl) {
|
||||
this.port = Geary.Smtp.ClientConnection.SUBMISSION_TLS_PORT;
|
||||
} else if (this.smtp_noauth) {
|
||||
this.port = Geary.Smtp.ClientConnection.SMTP_PORT;
|
||||
} else {
|
||||
this.port = Geary.Smtp.ClientConnection.SUBMISSION_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.smtp_noauth) {
|
||||
this.credentials = new Geary.Credentials(
|
||||
((GoaMediator) this.mediator).method,
|
||||
|
|
@ -61,4 +77,29 @@ public class GoaServiceInformation : Geary.ServiceInformation {
|
|||
return copy;
|
||||
}
|
||||
|
||||
private void parse_host_name(string host_name) {
|
||||
// Fall back to trying to use the host name as-is.
|
||||
// At least the user can see it in the settings if
|
||||
// they look.
|
||||
this.host = host_name;
|
||||
this.port = 0;
|
||||
|
||||
try {
|
||||
GLib.NetworkAddress address = GLib.NetworkAddress.parse(
|
||||
host_name, this.port
|
||||
);
|
||||
|
||||
this.host = address.hostname;
|
||||
this.port = (uint16) address.port;
|
||||
} catch (GLib.Error err) {
|
||||
warning(
|
||||
"GOA account \"%s\" %s hostname \"%s\": %",
|
||||
this.account.get_account().id,
|
||||
this.protocol.to_value(),
|
||||
host_name,
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,25 +257,33 @@ public class Geary.Engine : BaseObject {
|
|||
bool login_failed = false;
|
||||
imap_session.login_failed.connect(() => login_failed = true);
|
||||
|
||||
GLib.Error? login_err = null;
|
||||
try {
|
||||
yield imap_session.connect_async(cancellable);
|
||||
yield imap_session.initiate_session_async(
|
||||
account.imap.credentials, cancellable
|
||||
);
|
||||
} finally {
|
||||
try {
|
||||
yield imap_session.disconnect_async(cancellable);
|
||||
} catch {
|
||||
// Oh well
|
||||
}
|
||||
account.disconnect_service_endpoints();
|
||||
account.untrusted_host.disconnect(on_untrusted_host);
|
||||
} catch (GLib.Error err) {
|
||||
login_err = err;
|
||||
}
|
||||
|
||||
try {
|
||||
yield imap_session.disconnect_async(cancellable);
|
||||
} catch {
|
||||
// Oh well
|
||||
}
|
||||
|
||||
account.disconnect_service_endpoints();
|
||||
account.untrusted_host.disconnect(on_untrusted_host);
|
||||
|
||||
if (login_failed) {
|
||||
// XXX This should be a LOGIN_FAILED error or something
|
||||
throw new ImapError.UNAUTHENTICATED("Login failed");
|
||||
}
|
||||
|
||||
if (login_err != null) {
|
||||
throw login_err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -289,12 +297,10 @@ public class Geary.Engine : BaseObject {
|
|||
if (account.smtp.port == 0) {
|
||||
if (account.smtp.use_ssl) {
|
||||
account.smtp.port = Smtp.ClientConnection.SUBMISSION_TLS_PORT;
|
||||
} else if (account.smtp.use_starttls) {
|
||||
account.smtp.port = account.smtp.smtp_noauth
|
||||
? Smtp.ClientConnection.SMTP_PORT
|
||||
: Smtp.ClientConnection.SUBMISSION_PORT;
|
||||
} else {
|
||||
} else if (account.smtp.smtp_noauth) {
|
||||
account.smtp.port = Smtp.ClientConnection.SMTP_PORT;
|
||||
} else {
|
||||
account.smtp.port = Smtp.ClientConnection.SUBMISSION_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,18 +313,26 @@ public class Geary.Engine : BaseObject {
|
|||
account.smtp.endpoint
|
||||
);
|
||||
|
||||
GLib.Error? login_err = null;
|
||||
try {
|
||||
yield smtp_session.login_async(
|
||||
account.get_smtp_credentials(), cancellable
|
||||
);
|
||||
} finally {
|
||||
try {
|
||||
yield smtp_session.logout_async(true, cancellable);
|
||||
} catch {
|
||||
// Oh well
|
||||
}
|
||||
account.disconnect_service_endpoints();
|
||||
account.untrusted_host.disconnect(on_untrusted_host);
|
||||
} catch (GLib.Error err) {
|
||||
login_err = err;
|
||||
}
|
||||
|
||||
try {
|
||||
yield smtp_session.logout_async(true, cancellable);
|
||||
} catch {
|
||||
// Oh well
|
||||
}
|
||||
|
||||
account.disconnect_service_endpoints();
|
||||
account.untrusted_host.disconnect(on_untrusted_host);
|
||||
|
||||
if (login_err != null) {
|
||||
throw login_err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,76 @@
|
|||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkHeaderBar" id="header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title">Add an account</property>
|
||||
<property name="has_subtitle">False</property>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="back_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="icon_name">go-previous-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="create_spinner">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label" translatable="yes">Create</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_create_button_clicked" swapped="no"/>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="pane_adjustment">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
|
|
@ -170,63 +240,4 @@
|
|||
<class name="geary-accounts-editor-pane"/>
|
||||
</style>
|
||||
</template>
|
||||
<object class="GtkHeaderBar" id="header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title">Add an account</property>
|
||||
<property name="has_subtitle">False</property>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="back_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="icon_name">go-previous-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label" translatable="yes">Create</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_create_button_clicked" swapped="no"/>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue