2
* Copyright 2017 Michael Gratton <mike@vee.net>
4
* This software is licensed under the GNU Lesser General Public License
5
* (version 2.1 or later). See the COPYING file in this distribution.
10
* A popover for editing a link in the composer.
12
* The exact appearance of the popover will depend on the {@link
13
* Type} passed to the constructor:
15
* - For {@link Type.NEW_LINK}, the user will be presented with an
16
* insert button and an open button.
17
* - For {@link Type.EXISTING_LINK}, the user will be presented with
18
* an update, delete and open buttons.
20
[GtkTemplate (ui = "/org/gnome/Geary/composer-link-popover.ui")]
21
public class ComposerLinkPopover : Gtk.Popover {
23
private const string[] HTTP_SCHEMES = { "http", "https" };
24
private const string[] OTHER_SCHEMES = {
25
"aim", "apt", "bitcoin", "cvs", "ed2k", "ftp", "file", "finger",
26
"git", "gtalk", "irc", "ircs", "irc6", "lastfm", "ldap", "ldaps",
27
"magnet", "news", "nntp", "rsync", "sftp", "skype", "smb", "sms",
28
"svn", "telnet", "tftp", "ssh", "webcal", "xmpp"
31
/** Determines which version of the UI is presented to the user. */
33
/** A new link is being created. */
36
/** An existing link is being edited. */
40
/** The URL displayed in the popover */
41
public string link_uri { get { return this.url.get_text(); } }
44
private Gtk.Entry url;
47
private Gtk.Button insert;
50
private Gtk.Button update;
53
private Gtk.Button delete;
56
private Gtk.Button open;
58
private Geary.TimeoutManager validation_timeout;
61
/** Emitted when the link URL has changed. */
62
public signal void link_changed(Soup.URI? uri, bool is_valid);
64
/** Emitted when the link URL was activated. */
65
public signal void link_activate();
67
/** Emitted when the open button was activated. */
68
public signal void link_open();
70
/** Emitted when the delete button was activated. */
71
public signal void link_delete();
74
public ComposerLinkPopover(Type type) {
75
set_default_widget(this.url);
76
set_focus_child(this.url);
82
case Type.EXISTING_LINK:
86
this.validation_timeout = new Geary.TimeoutManager.milliseconds(
87
150, () => { validate(); }
91
public override void show() {
93
this.url.grab_focus();
96
public override void destroy() {
97
this.validation_timeout.reset();
101
public void set_link_url(string url) {
102
this.url.set_text(url);
103
this.validation_timeout.reset(); // Don't update on manual set
106
private void validate() {
107
string? text = this.url.get_text().strip();
108
bool is_empty = Geary.String.is_empty(text);
109
bool is_valid = false;
110
bool is_nominal = false;
111
bool is_mailto = false;
112
Soup.URI? url = null;
114
url = new Soup.URI(text);
118
string? scheme = url.get_scheme();
119
string? path = url.get_path();
120
if (scheme in HTTP_SCHEMES) {
121
is_nominal = Geary.Inet.is_valid_display_host(url.get_host());
122
} else if (scheme == "mailto") {
125
!Geary.String.is_empty(path) &&
126
Geary.RFC822.MailboxAddress.is_valid_address(path)
128
} else if (scheme in OTHER_SCHEMES) {
129
is_nominal = !Geary.String.is_empty(path);
131
} else if (text == "http:/" || text == "https:/") {
132
// Don't let the URL entry switch to invalid and back
133
// between "http:" and "http://"
138
// Don't let the user open invalid and mailto links, it's not
140
this.open.set_sensitive(is_nominal && !is_mailto);
142
Gtk.StyleContext style = this.url.get_style_context();
143
Gtk.EntryIconPosition pos = Gtk.EntryIconPosition.SECONDARY;
145
style.add_class(Gtk.STYLE_CLASS_ERROR);
146
style.remove_class(Gtk.STYLE_CLASS_WARNING);
147
this.url.set_icon_from_icon_name(pos, "dialog-error-symbolic");
148
this.url.set_tooltip_text(
149
_("Link URL is not correctly formatted, e.g. http://example.com")
151
} else if (!is_nominal) {
152
style.remove_class(Gtk.STYLE_CLASS_ERROR);
153
style.add_class(Gtk.STYLE_CLASS_WARNING);
154
this.url.set_icon_from_icon_name(pos, "dialog-warning-symbolic");
155
this.url.set_tooltip_text(
156
!is_mailto ? _("Invalid link URL") : _("Invalid email address")
159
style.remove_class(Gtk.STYLE_CLASS_ERROR);
160
style.remove_class(Gtk.STYLE_CLASS_WARNING);
161
this.url.set_icon_from_icon_name(pos, null);
162
this.url.set_tooltip_text("");
165
link_changed(url, is_valid && is_nominal);
169
private void on_url_changed() {
170
this.validation_timeout.start();
174
private void on_activate_popover() {
180
private void on_delete_clicked() {
186
private void on_open_clicked() {