Added files

This commit is contained in:
Gerben Jan Dijkman 2021-03-01 15:30:25 +01:00
parent 690bd18675
commit 63f719a806
136 changed files with 59478 additions and 0 deletions

View File

@ -0,0 +1,235 @@
From 3b6dd303323cfc5b7bebe5b1d88170f1030f2de2 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 5 Sep 2020 14:13:01 +1000
Subject: [PATCH 001/124] Geary.Db.Context: Update access to
DatabaseConnections
Ensure internal code can access a DatabaseConnection from context
objects to get access to connection-specific code, but make the
polymorphic context accessors internal so transactions can't access
them.
---
src/engine/db/db-connection.vala | 5 +++--
src/engine/db/db-context.vala | 8 ++++----
src/engine/db/db-database-connection.vala | 8 ++++----
src/engine/db/db-database.vala | 8 ++++----
src/engine/db/db-result.vala | 8 ++++----
src/engine/db/db-statement.vala | 19 ++++++++++++-------
src/engine/db/db-transaction-connection.vala | 11 +----------
src/engine/imap-db/imap-db-attachment.vala | 2 +-
8 files changed, 33 insertions(+), 36 deletions(-)
diff --git a/src/engine/db/db-connection.vala b/src/engine/db/db-connection.vala
index 4f0859e1..ebce27dc 100644
--- a/src/engine/db/db-connection.vala
+++ b/src/engine/db/db-connection.vala
@@ -18,7 +18,7 @@
* A connection will be automatically closed when its last reference
* is dropped.
*/
-public interface Geary.Db.Connection : Context {
+public interface Geary.Db.Connection : BaseObject {
private const string PRAGMA_FOREIGN_KEYS = "foreign_keys";
private const string PRAGMA_RECURSIVE_TRIGGERS = "recursive_triggers";
@@ -278,7 +278,8 @@ public interface Geary.Db.Connection : Context {
*
* @see exec
*/
- public abstract Result query(string sql, GLib.Cancellable? cancellable = null)
+ public abstract Result query(string sql,
+ GLib.Cancellable? cancellable = null)
throws GLib.Error;
/**
diff --git a/src/engine/db/db-context.vala b/src/engine/db/db-context.vala
index 9bbb8503..a59f6c4c 100644
--- a/src/engine/db/db-context.vala
+++ b/src/engine/db/db-context.vala
@@ -37,19 +37,19 @@ public abstract class Geary.Db.Context : BaseObject, Logging.Source {
private weak Logging.Source? _logging_parent = null;
- public virtual Database? get_database() {
+ internal virtual Database? get_database() {
return get_connection() != null ? get_connection().database : null;
}
- public virtual Connection? get_connection() {
+ internal virtual DatabaseConnection? get_connection() {
return get_statement() != null ? get_statement().connection : null;
}
- public virtual Statement? get_statement() {
+ internal virtual Statement? get_statement() {
return get_result() != null ? get_result().statement : null;
}
- public virtual Result? get_result() {
+ internal virtual Result? get_result() {
return null;
}
diff --git a/src/engine/db/db-database-connection.vala b/src/engine/db/db-database-connection.vala
index 4e7ceb78..dd311bea 100644
--- a/src/engine/db/db-database-connection.vala
+++ b/src/engine/db/db-database-connection.vala
@@ -255,13 +255,13 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
return yield job.wait_for_completion_async();
}
- public override Connection? get_connection() {
- return this;
- }
-
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
return new Logging.State(this, "%u", this.cx_number);
}
+ internal override DatabaseConnection? get_connection() {
+ return this;
+ }
+
}
diff --git a/src/engine/db/db-database.vala b/src/engine/db/db-database.vala
index 592bd306..a807e7ba 100644
--- a/src/engine/db/db-database.vala
+++ b/src/engine/db/db-database.vala
@@ -358,10 +358,6 @@ public class Geary.Db.Database : Context {
}
- public override Database? get_database() {
- return this;
- }
-
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
return new Logging.State(
@@ -386,6 +382,10 @@ public class Geary.Db.Database : Context {
this.thread_pool.add(new_job);
}
+ internal override Database? get_database() {
+ return this;
+ }
+
/**
* Hook for subclasses to modify a new SQLite connection before use.
*
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index 1ec3ed55..64c78756 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -294,15 +294,15 @@ public class Geary.Db.Result : Geary.Db.Context {
return column;
}
- public override Result? get_result() {
- return this;
- }
-
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
return new Logging.State(this, this.finished ? "finished" : "not finished");
}
+ internal override Result? get_result() {
+ return this;
+ }
+
[PrintfFormat]
private void log_result(string fmt, ...) {
if (Db.Context.enable_sql_logging) {
diff --git a/src/engine/db/db-statement.vala b/src/engine/db/db-statement.vala
index 0a36dfb1..088b882b 100644
--- a/src/engine/db/db-statement.vala
+++ b/src/engine/db/db-statement.vala
@@ -13,7 +13,7 @@ public class Geary.Db.Statement : Context {
get { return this.stmt.sql(); }
}
- public Connection connection { get; private set; }
+ internal DatabaseConnection connection { get; private set; }
internal Sqlite.Statement stmt;
@@ -36,9 +36,14 @@ public class Geary.Db.Statement : Context {
private Gee.HashSet<Memory.Buffer> held_buffers = new Gee.HashSet<Memory.Buffer>();
- internal Statement(Connection connection, string sql) throws DatabaseError {
+ internal Statement(DatabaseConnection connection, string sql)
+ throws DatabaseError {
this.connection = connection;
- throw_on_error("Statement.ctor", connection.db.prepare_v2(sql, -1, out stmt, null), sql);
+ throw_on_error(
+ "Statement.ctor",
+ connection.db.prepare_v2(sql, -1, out stmt, null),
+ sql
+ );
}
/** Returns SQL for the statement with bound parameters expanded. */
@@ -271,13 +276,13 @@ public class Geary.Db.Statement : Context {
return this;
}
- public override Statement? get_statement() {
- return this;
- }
-
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
return new Logging.State(this, this.sql);
}
+ internal override Statement? get_statement() {
+ return this;
+ }
+
}
diff --git a/src/engine/db/db-transaction-connection.vala b/src/engine/db/db-transaction-connection.vala
index 48244dbc..ebdd18b4 100644
--- a/src/engine/db/db-transaction-connection.vala
+++ b/src/engine/db/db-transaction-connection.vala
@@ -9,7 +9,7 @@
/**
* A connection to the database for transactions.
*/
-internal class Geary.Db.TransactionConnection : Context, Connection {
+internal class Geary.Db.TransactionConnection : BaseObject, Connection {
/** {@inheritDoc} */
@@ -54,13 +54,4 @@ internal class Geary.Db.TransactionConnection : Context, Connection {
this.db_cx.exec_file(file, cancellable);
}
- public override Connection? get_connection() {
- return this;
- }
-
- /** {@inheritDoc} */
- public override Logging.State to_logging_state() {
- return new Logging.State(this, "");
- }
-
}
diff --git a/src/engine/imap-db/imap-db-attachment.vala b/src/engine/imap-db/imap-db-attachment.vala
index d8e8f9db..fa94b630 100644
--- a/src/engine/imap-db/imap-db-attachment.vala
+++ b/src/engine/imap-db/imap-db-attachment.vala
@@ -245,7 +245,7 @@ private class Geary.ImapDB.Attachment : Geary.Attachment {
}
// Ensure they're dead, Jim.
- Db.Statement stmt = new Db.Statement(cx, """
+ Db.Statement stmt = cx.prepare("""
DELETE FROM MessageAttachmentTable WHERE message_id = ?
""");
stmt.bind_rowid(0, message_id);
--
2.29.2

View File

@ -0,0 +1,35 @@
From e6fd0fe1742bd2143c1fa16fa30e82a5ea7996ed Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 23 Apr 2020 10:11:06 +0200
Subject: [PATCH 1/8] accounts-editor: Wrap the welcome panel labels
This helps the accounts editor fit in narrow screens.
---
ui/accounts_editor_list_pane.ui | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui
index 59f8c632..a40604e3 100644
--- a/ui/accounts_editor_list_pane.ui
+++ b/ui/accounts_editor_list_pane.ui
@@ -60,6 +60,8 @@
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">To get started, select an email provider below.</property>
+ <property name="xalign">0</property>
+ <property name="wrap">True</property>
</object>
<packing>
<property name="left_attach">1</property>
@@ -73,6 +75,8 @@
<property name="halign">start</property>
<property name="valign">end</property>
<property name="label" translatable="yes">Welcome to Geary</property>
+ <property name="xalign">0</property>
+ <property name="wrap">True</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
--
2.29.2

View File

@ -0,0 +1,66 @@
From c0a89a86e48667b17dcae934e5f3b15a2475abf3 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Thu, 22 Oct 2020 15:47:09 +0200
Subject: [PATCH 1/2] conversation-email-row: use is-expanded to add/remove css
class
---
.../conversation-viewer/conversation-list-box.vala | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/client/conversation-viewer/conversation-list-box.vala b/src/client/conversation-viewer/conversation-list-box.vala
index 3eb8240b..f860a2df 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -284,6 +284,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
}
protected set {
this._is_expanded = value;
+ notify_property("is-expanded");
}
}
private bool _is_expanded = false;
@@ -301,6 +302,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
protected ConversationRow(Geary.Email? email) {
base_ref();
this.email = email;
+ notify["is-expanded"].connect(update_css_class);
show();
}
@@ -325,6 +327,13 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
this.size_allocate.connect(on_size_allocate);
}
+ private void update_css_class() {
+ if (this.is_expanded)
+ get_style_context().add_class(EXPANDED_CLASS);
+ else
+ get_style_context().remove_class(EXPANDED_CLASS);
+ }
+
protected inline void set_style_context_class(string class_name, bool value) {
if (value) {
get_style_context().add_class(class_name);
@@ -392,10 +401,8 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
private inline void update_row_expansion() {
if (this.is_expanded || this.is_pinned) {
- get_style_context().add_class(EXPANDED_CLASS);
this.view.expand_email();
} else {
- get_style_context().remove_class(EXPANDED_CLASS);
this.view.collapse_email();
}
}
@@ -436,7 +443,6 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
base(view.referred);
this.view = view;
this.is_expanded = true;
- get_style_context().add_class(EXPANDED_CLASS);
add(this.view);
}
--
2.29.2

View File

@ -0,0 +1,34 @@
From 0923de098f2e9ae6a94c5bb82e26b7d80c5181dc Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Fri, 9 Oct 2020 15:55:57 +0200
Subject: [PATCH 1/6] main-window: remove shadow from folder/conversation list
---
ui/application-main-window.ui | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index 547b063f..c55e42ab 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -49,7 +49,7 @@
<property name="can_focus">False</property>
<property name="vexpand">True</property>
<property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
+ <property name="shadow_type">none</property>
<child>
<object class="GtkScrolledWindow" id="folder_list_scrolled">
<property name="width_request">100</property>
@@ -95,7 +95,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
+ <property name="shadow_type">none</property>
<child>
<object class="GtkScrolledWindow" id="conversation_list_scrolled">
<property name="width_request">250</property>
--
2.29.2

View File

@ -0,0 +1,46 @@
From a1d31847b115e8ac81520226ff121fabc1f2e2ef Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 5 Sep 2020 14:15:28 +1000
Subject: [PATCH 002/124] Geary.Db.Result: Log large elapsed query times as a
warning
Help ensure that long-running queries get some visibility during
development.
---
src/engine/db/db-result.vala | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index 64c78756..300d3afa 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -38,10 +38,23 @@ public class Geary.Db.Result : Geary.Db.Context {
check_cancelled("Result.next", cancellable);
if (!finished) {
- Timer timer = new Timer();
+ var timer = new GLib.Timer();
finished = throw_on_error("Result.next", statement.stmt.step(), statement.sql) != Sqlite.ROW;
- if (timer.elapsed() > 1.0)
- debug("\n\nDB QUERY STEP \"%s\"\nelapsed=%lf\n\n", statement.sql, timer.elapsed());
+ var elapsed = timer.elapsed();
+ var threshold = (get_connection().busy_timeout * 1000.0) / 2.0;
+ if (threshold > 0 && elapsed > threshold) {
+ warning(
+ "Step for \"%s\" took elapsed time: %lfs (>50%)",
+ statement.sql,
+ elapsed
+ );
+ } else if (elapsed > 1.0) {
+ debug(
+ "Step for \"%s\" took elapsed time: %lfs (>1s)",
+ statement.sql,
+ elapsed
+ );
+ }
log_result(finished ? "NO ROW" : "ROW");
}
--
2.29.2

View File

@ -0,0 +1,24 @@
From c128b1be5f571315ae8a0f34cd9d00fd20c30cdc Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Mon, 27 Apr 2020 10:42:16 +0200
Subject: [PATCH 2/8] accounts-editor-add-pane: Drop the useless shadow
---
ui/accounts_editor_add_pane.ui | 1 -
1 file changed, 1 deletion(-)
diff --git a/ui/accounts_editor_add_pane.ui b/ui/accounts_editor_add_pane.ui
index 0c01a4c1..336b73ea 100644
--- a/ui/accounts_editor_add_pane.ui
+++ b/ui/accounts_editor_add_pane.ui
@@ -89,7 +89,6 @@
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
--
2.29.2

View File

@ -0,0 +1,235 @@
From 51da28b7c0ec32883b923f82b3d85ba4285dc623 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Fri, 23 Oct 2020 13:05:19 +0200
Subject: [PATCH 2/2] conversation-list-box: remove shadow and make the rows
rounded
This also makes the expander row look like in HdyExpander row.
---
.../conversation-list-box.vala | 56 +++++++++++++++++
.../conversation-web-view.vala | 26 ++++++++
ui/geary.css | 61 ++++++++++++-------
3 files changed, 120 insertions(+), 23 deletions(-)
diff --git a/src/client/conversation-viewer/conversation-list-box.vala b/src/client/conversation-viewer/conversation-list-box.vala
index f860a2df..f94ddea9 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -332,6 +332,33 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
get_style_context().add_class(EXPANDED_CLASS);
else
get_style_context().remove_class(EXPANDED_CLASS);
+
+ update_previous_sibling_css_class();
+ }
+
+ // This is mostly taken form libhandy HdyExpanderRow
+ private Gtk.Widget? get_previous_sibling() {
+ if (this.parent is Gtk.Container) {
+ var siblings = this.parent.get_children();
+ unowned List<weak Gtk.Widget> l;
+ for (l = siblings; l != null && l.next != null && l.next.data != this; l = l.next);
+
+ if (l != null && l.next != null && l.next.data == this) {
+ return l.data;
+ }
+ }
+
+ return null;
+ }
+
+ private void update_previous_sibling_css_class() {
+ var previous_sibling = get_previous_sibling();
+ if (previous_sibling != null) {
+ if (this.is_expanded)
+ previous_sibling.get_style_context().add_class("geary-expanded-previous-sibling");
+ else
+ previous_sibling.get_style_context().remove_class("geary-expanded-previous-sibling");
+ }
}
protected inline void set_style_context_class(string class_name, bool value) {
@@ -675,9 +702,14 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
this.selection_mode = NONE;
+ get_style_context().add_class("content");
get_style_context().add_class("background");
get_style_context().add_class("conversation-listbox");
+ /* we need to update the previous sibling style class when rows are added or removed */
+ add.connect(update_previous_sibling_css_class);
+ remove.connect(update_previous_sibling_css_class);
+
set_adjustment(adjustment);
set_sort_func(ConversationListBox.on_sort);
@@ -703,6 +735,30 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
base.destroy();
}
+ // For some reason insert doesn't emit the add event
+ public new void insert(Gtk.Widget child, int position) {
+ base.insert(child, position);
+ update_previous_sibling_css_class();
+ }
+
+ // This is mostly taken form libhandy HdyExpanderRow
+ private void update_previous_sibling_css_class() {
+ var siblings = this.get_children();
+ unowned List<weak Gtk.Widget> l;
+ for (l = siblings; l != null && l.next != null && l.next.data != this; l = l.next) {
+ if (l != null && l.next != null) {
+ var row = l.next.data as ConversationRow;
+ if (row != null) {
+ if (row.is_expanded) {
+ l.data.get_style_context().add_class("geary-expanded-previous-sibling");
+ } else {
+ l.data.get_style_context().remove_class("geary-expanded-previous-sibling");
+ }
+ }
+ }
+ }
+ }
+
public async void load_conversation(Gee.Collection<Geary.EmailIdentifier> scroll_to,
Geary.SearchQuery? query)
throws GLib.Error {
diff --git a/src/client/conversation-viewer/conversation-web-view.vala b/src/client/conversation-viewer/conversation-web-view.vala
index a1ba21a6..ad11415e 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -197,6 +197,32 @@ public class ConversationWebView : Components.WebView {
}
+ // Clip round bottom corner
+ // This is based on
+ // https://gitlab.gnome.org/GNOME/gnome-weather/-/commit/9b6336454cc90669d1ee8387bdfc6627e3659e83
+ public override bool draw(Cairo.Context cr) {
+ var frameWidth = this.get_allocated_width();
+ var frameHeight = this.get_allocated_height();
+ var borderRadius = 8;
+
+ var arc0 = 0.0;
+ var arc1 = Math.PI * 0.5;
+ var arc2 = Math.PI;
+
+ cr.new_sub_path();
+ cr.line_to(frameWidth, 0);
+ cr.arc(frameWidth - borderRadius, frameHeight - borderRadius, borderRadius, arc0, arc1);
+ cr.arc(borderRadius, frameHeight - borderRadius, borderRadius, arc1, arc2);
+ cr.line_to(0, 0);
+ cr.close_path();
+
+ cr.clip();
+ cr.fill();
+ base.draw(cr);
+
+ return Gdk.EVENT_PROPAGATE;
+ }
+
public override void get_preferred_height(out int minimum_height,
out int natural_height) {
// XXX clamp height to something not too outrageous so we
diff --git a/ui/geary.css b/ui/geary.css
index 2d1d48c3..78ade7c0 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -77,39 +77,44 @@ row.geary-folder-popover-list-row > label {
/* ConversationListBox */
.conversation-listbox {
- padding: 0 12px;
+ padding: 12px;
}
+
.conversation-listbox > row {
- margin: 0;
- border: 1px solid @borders;
- border-bottom-width: 0;
padding: 0;
- box-shadow: 0 4px 8px 1px rgba(0,0,0,0.4);
-}
-.conversation-listbox > row > box {
- background: @theme_base_color;
- transition: background 0.25s;
-}
-.conversation-listbox > row:hover > box {
- background: shade(@theme_base_color, 0.96);
-}
-.conversation-listbox > row.geary-expanded {
- margin-bottom: 6px;
- border-bottom-width: 1px;
}
+
.conversation-listbox *.geary-matched *.geary-match {
color: @theme_selected_fg_color;
background: @theme_selected_bg_color;
-;}
+}
+
.conversation-listbox > row.geary-loading {
border-top-width: 0px;
padding: 6px;
}
-.conversation-listbox > row:first-child:not(.geary-loading) {
- margin-top: 12px;
+
+.conversation-listbox.content > row:last-child,
+.conversation-listbox.content > row.geary-expanded-previous-sibling,
+.conversation-listbox.content > row.geary-expanded {
+ border-width: 1px;
}
-.conversation-listbox > row:last-child {
- margin-bottom: 12px;
+
+.geary-expanded, .geary-expanded + row {
+ border-top-left-radius: 8px;
+ -gtk-outline-top-left-radius: 7px;
+ border-top-right-radius: 8px;
+ -gtk-outline-top-right-radius: 7px;
+ margin-top: 6px;
+}
+
+.geary-expanded,
+.geary-expanded-previous-sibling {
+ border-bottom-left-radius: 8px;
+ -gtk-outline-bottom-left-radius: 7px;
+ border-bottom-right-radius: 8px;
+ -gtk-outline-bottom-right-radius: 7px;
+ margin-bottom: 6px
}
/* ConversationEmail */
@@ -119,6 +124,14 @@ row.geary-folder-popover-list-row > label {
transition: border 0.25s;
}
+.geary-expanded > .geary_email grid.geary-message-summary,
+.geary-expanded + row > .geary_email grid.geary-message-summary {
+ border-top-left-radius: 8px;
+ -gtk-outline-top-left-radius: 7px;
+ border-top-right-radius: 8px;
+ -gtk-outline-top-right-radius: 7px;
+}
+
/* ConversationMessage */
.geary-message infobar box {
@@ -198,8 +211,10 @@ grid.geary-message-summary {
/* Composer */
.geary-composer-embed headerbar {
- border-top: 1px solid @borders;
- border-radius: 0px;
+ border-top-left-radius: 8px;
+ -gtk-outline-top-left-radius: 7px;
+ border-top-right-radius: 8px;
+ -gtk-outline-top-right-radius: 7px;
}
.geary-attachments-box > box > box {
--
2.29.2

View File

@ -0,0 +1,258 @@
From 40824723c63d869535f89e628289fdb46cbf9c49 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Fri, 9 Oct 2020 17:20:31 +0200
Subject: [PATCH 2/6] conversation-viewer: move actions to the bottom when they
don't fit
---
.../application/application-main-window.vala | 8 ++-
.../components-conversation-action-bar.vala | 12 ++--
src/client/components/main-toolbar.vala | 63 +++++++++++++++----
.../conversation-viewer.vala | 1 -
ui/application-main-window.ui | 18 ++++++
ui/components-conversation-action-bar.ui | 21 ++++++-
6 files changed, 100 insertions(+), 23 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 90d5b249..19f04492 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -334,6 +334,10 @@ public class Application.MainWindow :
[GtkChild]
private Gtk.ScrolledWindow conversation_list_scrolled;
[GtkChild]
+ private Gtk.Box conversation_viewer_box;
+ [GtkChild]
+ private Components.ConversationActionBar conversation_viewer_action_bar;
+ [GtkChild]
private Gtk.SizeGroup folder_size_group;
[GtkChild]
private Gtk.SizeGroup folder_separator_size_group;
@@ -1266,7 +1270,7 @@ public class Application.MainWindow :
this.conversation_viewer.hexpand = true;
this.conversation_size_group.add_widget(this.conversation_viewer);
- this.main_leaflet.add_with_properties(this.conversation_viewer, "name", "conversation", null);
+ this.conversation_viewer_box.add(this.conversation_viewer);
// Setup conversation actions
@@ -1279,7 +1283,7 @@ public class Application.MainWindow :
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
// Main toolbar
- this.main_toolbar = new MainToolbar(config);
+ this.main_toolbar = new MainToolbar(config, conversation_viewer_action_bar);
this.main_toolbar.add_to_size_groups(this.folder_size_group,
this.folder_separator_size_group,
this.conversations_size_group,
diff --git a/src/client/components/components-conversation-action-bar.vala b/src/client/components/components-conversation-action-bar.vala
index cb574521..cd868b21 100644
--- a/src/client/components/components-conversation-action-bar.vala
+++ b/src/client/components/components-conversation-action-bar.vala
@@ -12,7 +12,7 @@ public class Components.ConversationActionBar : Gtk.Revealer {
private ulong owner_notify;
[GtkChild]
- private Gtk.Box action_box;
+ public Gtk.Box action_box;
public ConversationActionBar() {
}
@@ -23,17 +23,17 @@ public class Components.ConversationActionBar : Gtk.Revealer {
*/
public void add_conversation_actions(Components.ConversationActions actions) {
if (actions.owner == this)
- return;
+ return;
actions.take_ownership(this);
action_box.pack_start(actions.mark_copy_move_buttons, false, false);
action_box.pack_end(actions.archive_trash_delete_buttons, false, false);
reveal_child = true;
this.owner_notify = actions.notify["owner"].connect(() => {
- if (actions.owner != this) {
- reveal_child = false;
- actions.disconnect (this.owner_notify);
- }
+ if (actions.owner != this) {
+ reveal_child = false;
+ actions.disconnect (this.owner_notify);
+ }
});
}
}
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 6458b7fb..f216238a 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -17,6 +17,11 @@ public class MainToolbar : Hdy.Leaflet {
// Search bar
public bool search_open { get; set; default = false; }
+ private ulong owner_notify;
+ private Gtk.Widget? reply_forward_buttons;
+ private Gtk.Widget? archive_trash_delete_buttons;
+ private Components.ConversationActionBar conversation_viewer_action_bar;
+
[GtkChild]
private Hdy.Leaflet conversations_leaflet;
@@ -47,11 +52,14 @@ public class MainToolbar : Hdy.Leaflet {
Gtk.SizeGroup conversation_group;
- public MainToolbar(Application.Configuration config) {
+ public MainToolbar(Application.Configuration config,
+ Components.ConversationActionBar action_bar) {
if (config.desktop_environment != UNITY) {
this.bind_property("account", this.conversations_header, "title", BindingFlags.SYNC_CREATE);
this.bind_property("folder", this.conversations_header, "subtitle", BindingFlags.SYNC_CREATE);
}
+ this.conversation_viewer_action_bar = action_bar;
+ conversation_header.size_allocate.connect(on_size_allocate);
// Assemble the main/mark menus
Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui");
@@ -63,17 +71,6 @@ public class MainToolbar : Hdy.Leaflet {
BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
}
- public void add_conversation_actions(Components.ConversationActions actions) {
- if (actions.owner == this)
- return;
-
- actions.take_ownership(this);
- conversation_header.pack_start(actions.mark_copy_move_buttons);
- conversation_header.pack_start(actions.reply_forward_buttons);
- conversation_header.pack_end(actions.find_button);
- conversation_header.pack_end(actions.archive_trash_delete_buttons);
- }
-
public void set_conversation_header(Gtk.HeaderBar header) {
remove(conversation_header);
this.header_group.add_gtk_header_bar(header);
@@ -111,4 +108,46 @@ public class MainToolbar : Hdy.Leaflet {
conversations_group.add_swipeable(this.conversations_leaflet);
conversation_group.add_swipeable(this);
}
+
+ private void on_size_allocate() {
+ if (reply_forward_buttons != null && archive_trash_delete_buttons != null)
+ if (conversation_viewer_action_bar.reveal_child && get_allocated_width() > 600) {
+ conversation_viewer_action_bar.reveal_child = false;
+ remove_action_parent();
+ conversation_header.pack_start(reply_forward_buttons);
+ conversation_header.pack_end(archive_trash_delete_buttons);
+ } else if (!conversation_viewer_action_bar.reveal_child && get_allocated_width() < 600) {
+ remove_action_parent();
+ conversation_viewer_action_bar.action_box.pack_start(reply_forward_buttons, false, false);
+ conversation_viewer_action_bar.action_box.pack_end(archive_trash_delete_buttons, false, false);
+ conversation_viewer_action_bar.reveal_child = true;
+ }
+ }
+
+ private void remove_action_parent() {
+ if (reply_forward_buttons != null && reply_forward_buttons.parent != null)
+ reply_forward_buttons.parent.remove(reply_forward_buttons);
+ if (archive_trash_delete_buttons != null && archive_trash_delete_buttons.parent != null)
+ archive_trash_delete_buttons.parent.remove(archive_trash_delete_buttons);
+ }
+
+ public void add_conversation_actions(Components.ConversationActions actions) {
+ if (actions.owner != this) {
+ actions.take_ownership(this);
+ conversation_header.pack_start(actions.mark_copy_move_buttons);
+ conversation_header.pack_end(actions.find_button);
+
+ reply_forward_buttons = actions.reply_forward_buttons;
+ archive_trash_delete_buttons = actions.archive_trash_delete_buttons;
+ on_size_allocate();
+ this.owner_notify = actions.notify["owner"].connect(() => {
+ if (actions.owner != this) {
+ conversation_viewer_action_bar.reveal_child = false;
+ reply_forward_buttons = null;
+ archive_trash_delete_buttons = null;
+ actions.disconnect (this.owner_notify);
+ }
+ });
+ }
+ }
}
diff --git a/src/client/conversation-viewer/conversation-viewer.vala b/src/client/conversation-viewer/conversation-viewer.vala
index a5098764..74706f8c 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -532,5 +532,4 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
}
}
}
-
}
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index c55e42ab..1b41310d 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -137,6 +137,24 @@
<property name="navigatable">False</property>
</packing>
</child>
+ <child>
+ <object class="GtkBox" id="conversation_viewer_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="ComponentsConversationActionBar" id="conversation_viewer_action_bar">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">conversation</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">True</property>
diff --git a/ui/components-conversation-action-bar.ui b/ui/components-conversation-action-bar.ui
index ae49683f..6fc03f44 100644
--- a/ui/components-conversation-action-bar.ui
+++ b/ui/components-conversation-action-bar.ui
@@ -11,11 +11,28 @@
<property name="can_focus">False</property>
<property name="transition_type">slide-up</property>
<child>
- <object class="GtkBox" id="action_box">
+ <object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
- <property name="margin">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="action_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ </object>
+ </child>
</object>
</child>
</template>
--
2.29.2

View File

@ -0,0 +1,102 @@
From 485868d570ef95283c541a1a180fca88fec7a9ef Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Tue, 8 Sep 2020 08:34:23 +1000
Subject: [PATCH 003/124] Geary.Db.DatabaseConnection: Check elapsed time for
exec statements
Re-work elapsed timer to be usable in other context objects, use it
for timing exec query execution.
---
src/engine/db/db-context.vala | 13 +++++++++++++
src/engine/db/db-database-connection.vala | 9 ++++++---
src/engine/db/db-result.vala | 23 +++++------------------
3 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/src/engine/db/db-context.vala b/src/engine/db/db-context.vala
index a59f6c4c..6713f6c8 100644
--- a/src/engine/db/db-context.vala
+++ b/src/engine/db/db-context.vala
@@ -61,6 +61,19 @@ public abstract class Geary.Db.Context : BaseObject, Logging.Source {
/** {@inheritDoc} */
public abstract Logging.State to_logging_state();
+
+ protected inline void check_elapsed(string message,
+ GLib.Timer timer)
+ throws DatabaseError {
+ var elapsed = timer.elapsed();
+ var threshold = (get_connection().busy_timeout * 1000.0) / 2.0;
+ if (threshold > 0 && elapsed > threshold) {
+ warning("%s: elapsed time: %lfs (>50%)", message, elapsed);
+ } else if (elapsed > 1.0) {
+ debug("%s: elapsed time: %lfs (>1s)", message, elapsed);
+ }
+ }
+
protected inline int throw_on_error(string? method, int result, string? raw = null) throws DatabaseError {
return Db.throw_on_error(this, method, result, raw);
}
diff --git a/src/engine/db/db-database-connection.vala b/src/engine/db/db-database-connection.vala
index dd311bea..d58911e4 100644
--- a/src/engine/db/db-database-connection.vala
+++ b/src/engine/db/db-database-connection.vala
@@ -137,7 +137,9 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
}
check_cancelled("Connection.exec", cancellable);
- throw_on_error("Connection.exec", db.exec(sql), sql);
+ var timer = new GLib.Timer();
+ throw_on_error("Connection.exec_file", this.db.exec(sql), sql);
+ check_elapsed("Query \"%s\"".printf(sql), timer);
}
/** {@inheritDoc} */
@@ -147,8 +149,9 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
string sql;
FileUtils.get_contents(file.get_path(), out sql);
-
- exec(sql, cancellable);
+ var timer = new GLib.Timer();
+ throw_on_error("Connection.exec_file", this.db.exec(sql), sql);
+ check_elapsed(file.get_path(), timer);
}
/**
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index 300d3afa..b5382179 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -39,24 +39,11 @@ public class Geary.Db.Result : Geary.Db.Context {
if (!finished) {
var timer = new GLib.Timer();
- finished = throw_on_error("Result.next", statement.stmt.step(), statement.sql) != Sqlite.ROW;
- var elapsed = timer.elapsed();
- var threshold = (get_connection().busy_timeout * 1000.0) / 2.0;
- if (threshold > 0 && elapsed > threshold) {
- warning(
- "Step for \"%s\" took elapsed time: %lfs (>50%)",
- statement.sql,
- elapsed
- );
- } else if (elapsed > 1.0) {
- debug(
- "Step for \"%s\" took elapsed time: %lfs (>1s)",
- statement.sql,
- elapsed
- );
- }
-
- log_result(finished ? "NO ROW" : "ROW");
+ this.finished = throw_on_error(
+ "Result.next", statement.stmt.step(), statement.sql
+ ) != Sqlite.ROW;
+ check_elapsed("Result.next", timer);
+ log_result(this.finished ? "NO ROW" : "ROW");
}
return !finished;
--
2.29.2

View File

@ -0,0 +1,26 @@
From bab1759af038ecf08658b3664bf74428f47559e9 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Mon, 27 Apr 2020 10:43:07 +0200
Subject: [PATCH 3/8] accounts-editor-add-pane: Reduce the minimum entry width
This will help the pane fit in narrower widths.
---
src/client/accounts/accounts-editor-add-pane.vala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/client/accounts/accounts-editor-add-pane.vala b/src/client/accounts/accounts-editor-add-pane.vala
index a4a333cd..468e348f 100644
--- a/src/client/accounts/accounts-editor-add-pane.vala
+++ b/src/client/accounts/accounts-editor-add-pane.vala
@@ -526,7 +526,7 @@ private abstract class Accounts.EntryRow : AddPaneRow<Gtk.Entry> {
this.value.text = initial_value ?? "";
this.value.placeholder_text = placeholder ?? "";
- this.value.width_chars = 32;
+ this.value.width_chars = 16;
this.undo = new Components.EntryUndo(this.value);
}
--
2.29.2

View File

@ -0,0 +1,64 @@
From ee3e56aa1f3a71457b43a79b233ba3200e4e78d2 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Mon, 12 Oct 2020 16:35:22 +0200
Subject: [PATCH 3/6] conversation-viewer: allow one email per line
---
ui/conversation-message.ui | 6 ------
1 file changed, 6 deletions(-)
diff --git a/ui/conversation-message.ui b/ui/conversation-message.ui
index 95560939..d68817b2 100644
--- a/ui/conversation-message.ui
+++ b/ui/conversation-message.ui
@@ -157,7 +157,6 @@
<property name="valign">baseline</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">1</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
@@ -218,7 +217,6 @@
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">2</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
@@ -263,7 +261,6 @@
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">2</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
@@ -328,7 +325,6 @@
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">2</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
@@ -373,7 +369,6 @@
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">2</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
@@ -418,7 +413,6 @@
<property name="valign">start</property>
<property name="hexpand">True</property>
<property name="column_spacing">2</property>
- <property name="min_children_per_line">2</property>
<property name="max_children_per_line">4</property>
<property name="selection_mode">none</property>
<signal name="child-activated" handler="on_address_box_child_activated" swapped="no"/>
--
2.29.2

View File

@ -0,0 +1,66 @@
From 0fa0d0ea4d8db54166c131dee7b509d3984c2e2f Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 9 Sep 2020 18:30:22 +1000
Subject: [PATCH 004/124] Geary.Db.Statement: Minor code cleanup
Make `sql` a proper auto property. Remove expanded sql workaround.
Minor code style cleanup.
---
src/engine/db/db-statement.vala | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/src/engine/db/db-statement.vala b/src/engine/db/db-statement.vala
index 088b882b..4d792b42 100644
--- a/src/engine/db/db-statement.vala
+++ b/src/engine/db/db-statement.vala
@@ -9,15 +9,16 @@ private extern string? sqlite3_expanded_sql(Sqlite.Statement stmt);
public class Geary.Db.Statement : Context {
- public string sql {
- get { return this.stmt.sql(); }
- }
+
+ public string sql { get; private set; }
internal DatabaseConnection connection { get; private set; }
internal Sqlite.Statement stmt;
private Gee.HashMap<string, int>? column_map = null;
+ private Gee.HashSet<Memory.Buffer> held_buffers = new Gee.HashSet<Memory.Buffer>();
+
/**
* Fired when the Statement is executed the first time (after creation or after a reset).
@@ -34,11 +35,11 @@ public class Geary.Db.Statement : Context {
*/
public signal void bindings_cleared();
- private Gee.HashSet<Memory.Buffer> held_buffers = new Gee.HashSet<Memory.Buffer>();
internal Statement(DatabaseConnection connection, string sql)
throws DatabaseError {
this.connection = connection;
+ this.sql = sql;
throw_on_error(
"Statement.ctor",
connection.db.prepare_v2(sql, -1, out stmt, null),
@@ -48,13 +49,7 @@ public class Geary.Db.Statement : Context {
/** Returns SQL for the statement with bound parameters expanded. */
public string? get_expanded_sql() {
- // Replace all this with `Sqlite.Statement.expanded_sql` is
- // readily available. See:
- // https://gitlab.gnome.org/GNOME/vala/merge_requests/74
- string* sqlite = sqlite3_expanded_sql(this.stmt);
- string? sql = sqlite;
- Sqlite.Memory.free((void*) sqlite);
- return sql;
+ return this.stmt.expanded_sql();
}
/**
--
2.29.2

View File

@ -0,0 +1,28 @@
From 84c94463cd806ac5dbb1692ef3c04107b5a5f12f Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Mon, 27 Apr 2020 10:43:45 +0200
Subject: [PATCH 4/8] accounts-editor-eit-pane: Ellipsize the account row label
This will help the pane fit in narrower widths.
---
src/client/accounts/accounts-editor-edit-pane.vala | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/client/accounts/accounts-editor-edit-pane.vala b/src/client/accounts/accounts-editor-edit-pane.vala
index 2722db6e..5b944333 100644
--- a/src/client/accounts/accounts-editor-edit-pane.vala
+++ b/src/client/accounts/accounts-editor-edit-pane.vala
@@ -376,7 +376,9 @@ private class Accounts.MailboxRow : AccountRow<EditorEditPane,Gtk.Label> {
public MailboxRow(Geary.AccountInformation account,
Geary.RFC822.MailboxAddress mailbox) {
- base(account, "", new Gtk.Label(""));
+ var label = new Gtk.Label("");
+ label.ellipsize = Pango.EllipsizeMode.END;
+ base(account, "", label);
this.mailbox = mailbox;
enable_drag();
--
2.29.2

View File

@ -0,0 +1,558 @@
From 7d164ce964aa4118d019a741f9abf821f1897985 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 13 Oct 2020 15:46:39 +0200
Subject: [PATCH 4/6] compnents-info-bar: use custom infobar so that the
buttons reflow
---
po/POTFILES.in | 1 +
.../application/application-main-window.vala | 2 +-
.../components/components-info-bar-stack.vala | 34 ++--
.../components/components-info-bar.vala | 153 ++++++++++++++++--
.../conversation-list-box.vala | 4 +-
.../conversation-message.vala | 4 +-
ui/components-info-bar.ui | 83 ++++++++++
ui/geary.css | 4 +
ui/org.gnome.Geary.gresource.xml | 1 +
9 files changed, 248 insertions(+), 38 deletions(-)
create mode 100644 ui/components-info-bar.ui
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cd8b339d..6ab344dc 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -460,6 +460,7 @@ ui/components-attachment-view.ui
ui/components-conversation-actions.ui
ui/components-conversation-action-bar.ui
ui/components-in-app-notification.ui
+ui/components-info-bar.ui
ui/components-inspector-error-view.ui
ui/components-inspector-log-view.ui
ui/components-inspector.ui
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 19f04492..27526e0e 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -889,7 +889,7 @@ public class Application.MainWindow :
}
/** Displays an infobar in the window. */
- public void show_info_bar(Gtk.InfoBar info_bar) {
+ public void show_info_bar(Components.InfoBar info_bar) {
if (!this.info_bars.has_current) {
this.info_bars.add(info_bar);
}
diff --git a/src/client/components/components-info-bar-stack.vala b/src/client/components/components-info-bar-stack.vala
index cbe63e05..83339210 100644
--- a/src/client/components/components-info-bar-stack.vala
+++ b/src/client/components/components-info-bar-stack.vala
@@ -6,7 +6,7 @@
*/
/**
- * A stack-like widget for displaying Gtk InfoBar widgets.
+ * A stack-like widget for displaying Components.InfoBar widgets.
*
* The stack ensures only one info bar is shown at once, shows a frame
* around the info bar, and manages revealing and hiding itself and
@@ -40,7 +40,7 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
}
- private class SingletonQueue : Gee.AbstractQueue<Gtk.InfoBar> {
+ private class SingletonQueue : Gee.AbstractQueue<Components.InfoBar> {
public override bool read_only {
get { return false; }
@@ -62,10 +62,10 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
get { return (this.element != null) ? 0 : 1; }
}
- private Gtk.InfoBar? element = null;
+ private Components.InfoBar? element = null;
- public override bool add(Gtk.InfoBar to_add) {
+ public override bool add(Components.InfoBar to_add) {
var added = false;
if (this.element != to_add) {
this.element = to_add;
@@ -78,20 +78,20 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
this.element = null;
}
- public override bool contains(Gtk.InfoBar other) {
+ public override bool contains(Components.InfoBar other) {
return (this.element == other);
}
- public override Gee.Iterator<Gtk.InfoBar> iterator() {
+ public override Gee.Iterator<Components.InfoBar> iterator() {
// This sucks but it won't ever be used so oh well
return (
this.element == null
- ? Gee.Collection.empty<Gtk.InfoBar>().iterator()
+ ? Gee.Collection.empty<Components.InfoBar>().iterator()
: Geary.Collection.single(this.element).iterator()
);
}
- public override bool remove(Gtk.InfoBar to_remove) {
+ public override bool remove(Components.InfoBar to_remove) {
var removed = false;
if (this.element == to_remove) {
this.element = null;
@@ -100,11 +100,11 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
return removed;
}
- public override Gtk.InfoBar peek() {
+ public override Components.InfoBar peek() {
return this.element;
}
- public override Gtk.InfoBar poll() {
+ public override Components.InfoBar poll() {
var element = this.element;
this.element = null;
return element;
@@ -126,7 +126,7 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
* @see algorithm
* @see StackType.PRIORITY_QUEUE
*/
- public static int priority_queue_comparator(Gtk.InfoBar a, Gtk.InfoBar b) {
+ public static int priority_queue_comparator(Components.InfoBar a, Components.InfoBar b) {
return (
b.get_data<int>(PRIORITY_QUEUE_KEY) -
a.get_data<int>(PRIORITY_QUEUE_KEY)
@@ -150,11 +150,11 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
}
/** Returns the currently displayed info bar, if any. */
- public Gtk.InfoBar? current_info_bar {
- get { return get_child() as Gtk.InfoBar; }
+ public Components.InfoBar? current_info_bar {
+ get { return get_child() as Components.InfoBar; }
}
- private Gee.Queue<Gtk.InfoBar> available;
+ private Gee.Queue<Components.InfoBar> available;
private int last_allocated_height = 0;
@@ -175,7 +175,7 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
* stack constructed, the info bar may or may not be revealed
* immediately.
*/
- public new void add(Gtk.InfoBar to_add) {
+ public new void add(Components.InfoBar to_add) {
if (this.available.offer(to_add)) {
update();
}
@@ -188,7 +188,7 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
* replaced with the next info bar added. If the only info bar
* present is removed, the stack also hides itself.
*/
- public new void remove(Gtk.InfoBar to_remove) {
+ public new void remove(Components.InfoBar to_remove) {
if (this.available.remove(to_remove)) {
update();
}
@@ -234,7 +234,7 @@ public class Components.InfoBarStack : Gtk.Frame, Geary.BaseInterface {
this.available = new SingletonQueue();
break;
case PRIORITY_QUEUE:
- this.available = new Gee.PriorityQueue<Gtk.InfoBar>(
+ this.available = new Gee.PriorityQueue<Components.InfoBar>(
InfoBarStack.priority_queue_comparator
);
break;
diff --git a/src/client/components/components-info-bar.vala b/src/client/components/components-info-bar.vala
index 05124c00..4ab6b56d 100644
--- a/src/client/components/components-info-bar.vala
+++ b/src/client/components/components-info-bar.vala
@@ -8,9 +8,11 @@
/**
* A standard info bar widget with status message and description.
*/
-public class Components.InfoBar : Gtk.InfoBar {
+[GtkTemplate (ui = "/org/gnome/Geary/components-info-bar.ui")]
+public class Components.InfoBar : Gtk.Box {
+ public signal void response(int response_id);
/**
* A short, human-readable status message.
*
@@ -26,11 +28,38 @@ public class Components.InfoBar : Gtk.InfoBar {
*/
public Gtk.Label? description { get; private set; default = null; }
+ public bool show_close_button { get; set; default = false;}
+ public bool revealed { get; set; }
+ private Gtk.MessageType _message_type;
+ public Gtk.MessageType message_type {
+ get {
+ return _message_type;
+ }
+ set {
+ _set_message_type(value);
+ }
+ }
+
private Plugin.InfoBar? plugin = null;
private string? plugin_action_group_name = null;
private Gtk.Button? plugin_primary_button = null;
+ [GtkChild]
+ private Gtk.Revealer revealer;
+
+ [GtkChild]
+ private Gtk.Box action_area;
+
+ [GtkChild]
+ private Gtk.Box content_area;
+
+ [GtkChild]
+ private Gtk.Button close_button;
+
+ static construct {
+ set_css_name("infobar");
+ }
/**
* Constructs a new info bar.
@@ -43,6 +72,20 @@ public class Components.InfoBar : Gtk.InfoBar {
public InfoBar(string status, string? description = null) {
this.status = new Gtk.Label(status);
this.status.halign = START;
+ this.status.xalign = 0;
+
+ _message_type = Gtk.MessageType.OTHER;
+ _set_message_type(Gtk.MessageType.INFO);
+
+ this.bind_property("revealed",
+ this.revealer,
+ "reveal-child",
+ BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+
+ this.bind_property("show-close-button",
+ this.close_button,
+ "visible",
+ BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
var attrs = new Pango.AttrList();
attrs.change(Pango.attr_weight_new(BOLD));
@@ -57,11 +100,8 @@ public class Components.InfoBar : Gtk.InfoBar {
this.description = new Gtk.Label(description);
this.description.halign = START;
this.description.valign = START;
-
- // Set the description to be ellipsised and set and the
- // tool-tip to be the same, in case it is too long for the
- // info bar's width
- this.description.ellipsize = END;
+ this.description.xalign = 0;
+ this.description.wrap = true;
this.description.tooltip_text = description;
}
@@ -85,15 +125,28 @@ public class Components.InfoBar : Gtk.InfoBar {
this.plugin_action_group_name = action_group_name;
this.show_close_button = plugin.show_close_button;
+ _message_type = Gtk.MessageType.OTHER;
+ _set_message_type(Gtk.MessageType.INFO);
+
+ this.bind_property("revealed",
+ this.revealer,
+ "reveal-child",
+ BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+
+ this.bind_property("show-close-button",
+ this.close_button,
+ "visible",
+ BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+
plugin.notify["status"].connect(
() => { this.status.label = plugin.status; }
- );
+ );
plugin.notify["description"].connect(
() => { this.description.label = plugin.description; }
- );
+ );
plugin.notify["primary-button"].connect(
() => { this.update_plugin_primary_button(); }
- );
+ );
var secondaries = plugin.secondary_buttons.bidir_list_iterator();
bool has_prev = secondaries.last();
@@ -108,11 +161,12 @@ public class Components.InfoBar : Gtk.InfoBar {
show_all();
}
- /* {@inheritDoc} */
- public override void response(int response) {
- if (response == Gtk.ResponseType.CLOSE && this.plugin != null) {
+ [GtkCallback]
+ public void on_close_button_clicked() {
+ if (this.plugin != null) {
this.plugin.close_activated();
}
+ response(Gtk.ResponseType.CLOSE);
}
/* {@inheritDoc} */
@@ -120,10 +174,22 @@ public class Components.InfoBar : Gtk.InfoBar {
this.plugin = null;
}
- // GTK 3.24.16 fixed the binding for this, but that and the VAPI
- // change has yet to trickle down to common distros like F31
- public new Gtk.Box get_action_area() {
- return (Gtk.Box) base.get_action_area();
+ public Gtk.Box get_action_area() {
+ return this.action_area;
+ }
+
+ public Gtk.Box get_content_area() {
+ return this.content_area;
+ }
+
+ public Gtk.Button add_button(string button_text, int response_id) {
+ var button = new Gtk.Button.with_mnemonic(button_text);
+ button.clicked.connect(() => {
+ response(response_id);
+ });
+ get_action_area().add(button);
+ button.visible = true;
+ return button;
}
private void update_plugin_primary_button() {
@@ -162,4 +228,59 @@ public class Components.InfoBar : Gtk.InfoBar {
return button;
}
+ private void _set_message_type(Gtk.MessageType message_type) {
+ if (this._message_type != message_type) {
+ Gtk.StyleContext context = this.get_style_context();
+ const string[] type_class = {
+ Gtk.STYLE_CLASS_INFO,
+ Gtk.STYLE_CLASS_WARNING,
+ Gtk.STYLE_CLASS_QUESTION,
+ Gtk.STYLE_CLASS_ERROR,
+ null
+ };
+
+ if (type_class[this._message_type] != null)
+ context.remove_class(type_class[this._message_type]);
+
+ this._message_type = message_type;
+
+ var atk_obj = this.get_accessible();
+ if (atk_obj is Atk.Object) {
+ string name = null;
+
+ atk_obj.set_role(Atk.Role.INFO_BAR);
+
+ switch (message_type) {
+ case Gtk.MessageType.INFO:
+ name = _("Information");
+ break;
+
+ case Gtk.MessageType.QUESTION:
+ name = _("Question");
+ break;
+
+ case Gtk.MessageType.WARNING:
+ name = _("Warning");
+ break;
+
+ case Gtk.MessageType.ERROR:
+ name = _("Error");
+ break;
+
+ case Gtk.MessageType.OTHER:
+ break;
+
+ default:
+ warning("Unknown GtkMessageType %u", message_type);
+ break;
+ }
+
+ if (name != null)
+ atk_obj.set_name(name);
+ }
+
+ if (type_class[this._message_type] != null)
+ context.add_class(type_class[this._message_type]);
+ }
+ }
}
diff --git a/src/client/conversation-viewer/conversation-list-box.vala b/src/client/conversation-viewer/conversation-list-box.vala
index 3eb8240b..7d0c94af 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -936,7 +936,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
/** Adds an info bar to the given email, if any. */
public void add_email_info_bar(Geary.EmailIdentifier id,
- Gtk.InfoBar info_bar) {
+ Components.InfoBar info_bar) {
var row = this.email_rows.get(id);
if (row != null) {
row.view.primary_message.info_bars.add(info_bar);
@@ -945,7 +945,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
/** Adds an info bar to the given email, if any. */
public void remove_email_info_bar(Geary.EmailIdentifier id,
- Gtk.InfoBar info_bar) {
+ Components.InfoBar info_bar) {
var row = this.email_rows.get(id);
if (row != null) {
row.view.primary_message.info_bars.remove(info_bar);
diff --git a/src/client/conversation-viewer/conversation-message.vala b/src/client/conversation-viewer/conversation-message.vala
index 109c4a1c..868fea7e 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -380,7 +380,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
[GtkChild]
private Gtk.ProgressBar body_progress;
- private Gtk.InfoBar? remote_images_info_bar = null;
+ private Components.InfoBar? remote_images_info_bar = null;
private Gtk.Widget? body_placeholder = null;
@@ -1460,7 +1460,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
}
}
- private void on_remote_images_response(Gtk.InfoBar info_bar, int response_id) {
+ private void on_remote_images_response(Components.InfoBar info_bar, int response_id) {
switch (response_id) {
case 1:
// Show images for the message
diff --git a/ui/components-info-bar.ui b/ui/components-info-bar.ui
new file mode 100644
index 00000000..11dcfe37
--- /dev/null
+++ b/ui/components-info-bar.ui
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="ComponentsInfoBar" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition_type">slide-down</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkFlowBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selection-mode">none</property>
+ <property name="max_children_per_line">2</property>
+ <property name="border-width">12</property>
+ <child>
+ <object class="GtkFlowBoxChild">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="content_area">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">16</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFlowBoxChild">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkButtonBox" id="action_area">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <property name="spacing">6</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin">6</property>
+ <property name="no_show_all">True</property>
+ <signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
+ <style>
+ <class name="titlebutton"/>
+ <class name="close"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">window-close-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/ui/geary.css b/ui/geary.css
index 2d1d48c3..e38d135a 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -62,6 +62,10 @@ geary-conversation-viewer {
border-right-width: 0;
}
+infobar flowboxchild {
+ padding: 0px;
+}
+
/* FolderPopover */
row.geary-folder-popover-list-row {
diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml
index 0b9e900f..fbd7899d 100644
--- a/ui/org.gnome.Geary.gresource.xml
+++ b/ui/org.gnome.Geary.gresource.xml
@@ -16,6 +16,7 @@
<file compressed="true" preprocess="xml-stripblanks">components-conversation-action-bar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-conversation-actions.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-in-app-notification.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks">components-info-bar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-inspector.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-inspector-error-view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-inspector-log-view.ui</file>
--
2.29.2

View File

@ -0,0 +1,150 @@
From 940ca83195ba1f145a70b9dd0246f4d5aa2a069d Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 9 Sep 2020 18:33:44 +1000
Subject: [PATCH 005/124] Geary.Db.Context: Remove separate `logging_parent`
property
Since each context type already has access to the object that is its
context parent, don't bother with a stand-alone `logging_parent`
property, just have context types implement it and return the
appropriate object.
---
src/engine/db/db-context.vala | 8 +-------
src/engine/db/db-database-connection.vala | 6 +++++-
src/engine/db/db-database.vala | 10 ++++++++--
src/engine/db/db-result.vala | 6 ++++--
src/engine/db/db-statement.vala | 5 +++++
5 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/src/engine/db/db-context.vala b/src/engine/db/db-context.vala
index 6713f6c8..2ba8b305 100644
--- a/src/engine/db/db-context.vala
+++ b/src/engine/db/db-context.vala
@@ -33,8 +33,7 @@ public abstract class Geary.Db.Context : BaseObject, Logging.Source {
}
/** {@inheritDoc} */
- public Logging.Source? logging_parent { get { return _logging_parent; } }
- private weak Logging.Source? _logging_parent = null;
+ public abstract Logging.Source? logging_parent { get; }
internal virtual Database? get_database() {
@@ -53,11 +52,6 @@ public abstract class Geary.Db.Context : BaseObject, Logging.Source {
return null;
}
- /** {@inheritDoc} */
- public void set_logging_parent(Logging.Source parent) {
- this._logging_parent = parent;
- }
-
/** {@inheritDoc} */
public abstract Logging.State to_logging_state();
diff --git a/src/engine/db/db-database-connection.vala b/src/engine/db/db-database-connection.vala
index d58911e4..54d36160 100644
--- a/src/engine/db/db-database-connection.vala
+++ b/src/engine/db/db-database-connection.vala
@@ -66,6 +66,11 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
public Database database { get { return this._database; } }
private weak Database _database;
+ /** {@inheritDoc} */
+ public override Logging.Source? logging_parent {
+ get { return this._database; }
+ }
+
/** {@inheritDoc} */
internal Sqlite.Database db { get { return this._db; } }
private Sqlite.Database _db;
@@ -119,7 +124,6 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
/** {@inheritDoc} */
public Statement prepare(string sql) throws DatabaseError {
var prepared = new Statement(this, sql);
- prepared.set_logging_parent(this);
return prepared;
}
diff --git a/src/engine/db/db-database.vala b/src/engine/db/db-database.vala
index a807e7ba..df5bed21 100644
--- a/src/engine/db/db-database.vala
+++ b/src/engine/db/db-database.vala
@@ -57,6 +57,10 @@ public class Geary.Db.Database : Context {
}
}
+ /** {@inheritDoc} */
+ public override Logging.Source? logging_parent { get { return _logging_parent; } }
+ private weak Logging.Source? _logging_parent = null;
+
private DatabaseConnection? primary = null;
private int outstanding_async_jobs = 0;
private ThreadPool<TransactionAsyncJob>? thread_pool = null;
@@ -143,7 +147,6 @@ public class Geary.Db.Database : Context {
var cx = new DatabaseConnection(
this, Sqlite.OPEN_READWRITE, cancellable
);
- cx.set_logging_parent(this);
try {
// drop existing test table (in case created in prior failed open)
@@ -233,7 +236,6 @@ public class Geary.Db.Database : Context {
DatabaseConnection cx = new DatabaseConnection(
this, sqlite_flags, cancellable
);
- cx.set_logging_parent(this);
prepare_connection(cx);
return cx;
}
@@ -357,6 +359,10 @@ public class Geary.Db.Database : Context {
return yield job.wait_for_completion_async();
}
+ /** Sets the logging parent context object for this database. */
+ public void set_logging_parent(Logging.Source parent) {
+ this._logging_parent = parent;
+ }
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index b5382179..8c40c475 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -10,12 +10,14 @@ public class Geary.Db.Result : Geary.Db.Context {
public Statement statement { get; private set; }
+ /** {@inheritDoc} */
+ public override Logging.Source? logging_parent {
+ get { return this.statement; }
+ }
// This results in an automatic first next().
internal Result(Statement statement, Cancellable? cancellable) throws Error {
this.statement = statement;
- set_logging_parent(statement);
-
statement.was_reset.connect(on_query_finished);
statement.bindings_cleared.connect(on_query_finished);
diff --git a/src/engine/db/db-statement.vala b/src/engine/db/db-statement.vala
index 4d792b42..072692ff 100644
--- a/src/engine/db/db-statement.vala
+++ b/src/engine/db/db-statement.vala
@@ -12,6 +12,11 @@ public class Geary.Db.Statement : Context {
public string sql { get; private set; }
+ /** {@inheritDoc} */
+ public override Logging.Source? logging_parent {
+ get { return this.connection; }
+ }
+
internal DatabaseConnection connection { get; private set; }
internal Sqlite.Statement stmt;
--
2.29.2

View File

@ -0,0 +1,38 @@
From 7c9d1258c6a952c7b7f79f34feb3225dea3fcc15 Mon Sep 17 00:00:00 2001
From: Arnaud Ferraris <arnaud.ferraris@collabora.com>
Date: Wed, 25 Mar 2020 14:40:11 +0100
Subject: [PATCH 5/8] accounts-editor: make window usable on phones
---
ui/accounts_editor.ui | 2 +-
ui/accounts_editor_list_pane.ui | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/ui/accounts_editor.ui b/ui/accounts_editor.ui
index 4e23cdd5..04581e8d 100644
--- a/ui/accounts_editor.ui
+++ b/ui/accounts_editor.ui
@@ -5,7 +5,7 @@
<template class="AccountsEditor" parent="GtkDialog">
<property name="can_focus">False</property>
<property name="modal">True</property>
- <property name="default_width">700</property>
+ <property name="default_width">360</property>
<property name="default_height">450</property>
<property name="type_hint">dialog</property>
<child type="titlebar">
diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui
index a40604e3..b3946196 100644
--- a/ui/accounts_editor_list_pane.ui
+++ b/ui/accounts_editor_list_pane.ui
@@ -59,6 +59,7 @@
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
+ <property name="wrap">True</property>
<property name="label" translatable="yes">To get started, select an email provider below.</property>
<property name="xalign">0</property>
<property name="wrap">True</property>
--
2.29.2

View File

@ -0,0 +1,37 @@
From d1800de4dac6911b414b223318c6b12549e3a887 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Wed, 21 Oct 2020 11:18:19 +0200
Subject: [PATCH 5/6] conversation-viewer: don't show action-bar when in
composer
---
src/client/components/main-toolbar.vala | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index f216238a..8255c93b 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -60,6 +60,7 @@ public class MainToolbar : Hdy.Leaflet {
}
this.conversation_viewer_action_bar = action_bar;
conversation_header.size_allocate.connect(on_size_allocate);
+ conversation_header.notify["parent"].connect(on_size_allocate);
// Assemble the main/mark menus
Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui");
@@ -110,7 +111,10 @@ public class MainToolbar : Hdy.Leaflet {
}
private void on_size_allocate() {
- if (reply_forward_buttons != null && archive_trash_delete_buttons != null)
+ /* Only show the action_bar when the conversation_header is shown */
+ if (conversation_header.parent == null)
+ conversation_viewer_action_bar.reveal_child = false;
+ else if (reply_forward_buttons != null && archive_trash_delete_buttons != null)
if (conversation_viewer_action_bar.reveal_child && get_allocated_width() > 600) {
conversation_viewer_action_bar.reveal_child = false;
remove_action_parent();
--
2.29.2

View File

@ -0,0 +1,28 @@
From e39853db6215ac720dd047f48d940236cdbaa7f1 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 9 Sep 2020 18:38:08 +1000
Subject: [PATCH 006/124] Geary.ImapEngine.GenericAccount: Set database logging
parent per account
Set the database's logging parent so its log statements are associated
with its account in the inspector.
---
src/engine/imap-engine/imap-engine-generic-account.vala | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala
index 2bd087b1..ef1ba7b4 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -79,6 +79,8 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
base(config, imap, smtp);
this.local = local;
+ this.local.db.set_logging_parent(this);
+
this.contact_store = new ContactStoreImpl(local.db);
imap.min_pool_size = IMAP_MIN_POOL_SIZE;
--
2.29.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
From 805eb739e94e84ff3566283b9a89dcacc7b871e8 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Thu, 22 Oct 2020 12:33:58 +0200
Subject: [PATCH 6/6] in-app-notification: wrap text and add start/end margin
This makes sure that the in-app-notification fits also small window
sizes and eventually wraps the text to a new.
---
ui/components-in-app-notification.ui | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ui/components-in-app-notification.ui b/ui/components-in-app-notification.ui
index 54a1dafd..adbb3e2f 100644
--- a/ui/components-in-app-notification.ui
+++ b/ui/components-in-app-notification.ui
@@ -5,6 +5,8 @@
<property name="visible">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
<signal name="notify::child-revealed" handler="on_child_revealed" swapped="no"/>
<child>
<object class="GtkBox" id="layout">
@@ -17,18 +19,21 @@
<child>
<object class="GtkLabel" id="message_label">
<property name="visible">True</property>
+ <property name="wrap">True</property>
</object>
</child>
<child>
<object class="GtkButton" id="action_button">
<property name="visible">False</property>
<property name="can_focus">False</property>
+ <property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="valign">center</property>
<signal name="clicked" handler="close" swapped="no"/>
<style>
<class name="flat"/>
--
2.29.2

View File

@ -0,0 +1,157 @@
From 0f60c285df50586058f9368c29f7b288c8076b76 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 9 Sep 2020 18:48:43 +1000
Subject: [PATCH 007/124] Geary.Db: Update SQL logging
Ensure all code paths that execute SQL are logging it if enabled, and
clean up the log formatting.
Add new `enable_result_logging` static property to control result
logging separately from SQL logging, so we can get SQL logged without
having to see email. Clean up result formatting, and keep track of the
current result row number and log that as context.
---
src/engine/db/db-context.vala | 17 ++++++++++----
src/engine/db/db-database-connection.vala | 14 +++++++-----
src/engine/db/db-result.vala | 27 +++++++++++++----------
3 files changed, 37 insertions(+), 21 deletions(-)
diff --git a/src/engine/db/db-context.vala b/src/engine/db/db-context.vala
index 2ba8b305..7b8b9d65 100644
--- a/src/engine/db/db-context.vala
+++ b/src/engine/db/db-context.vala
@@ -16,16 +16,25 @@
public abstract class Geary.Db.Context : BaseObject, Logging.Source {
+ /** The GLib logging domain used by this class. */
+ public const string LOGGING_DOMAIN = Logging.DOMAIN + ".Db";
+
+
/**
- * Determines if SQL queries and results will be logged.
+ * Determines if SQL queries will be logged.
*
- * This will cause extremely verbose logging, so enable with care.
+ * This will cause verbose logging, so enable with care.
*/
public static bool enable_sql_logging = false;
- /** The GLib logging domain used by this class. */
- public const string LOGGING_DOMAIN = Logging.DOMAIN + ".Db";
+ /**
+ * Determines if SQL results will be logged.
+ *
+ * This will cause extremely verbose logging, so enable with extra care.
+ */
+ public static bool enable_result_logging = false;
+
/** {@inheritDoc} */
public string logging_domain {
diff --git a/src/engine/db/db-database-connection.vala b/src/engine/db/db-database-connection.vala
index 54d36160..29d52fd1 100644
--- a/src/engine/db/db-database-connection.vala
+++ b/src/engine/db/db-database-connection.vala
@@ -123,8 +123,10 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
/** {@inheritDoc} */
public Statement prepare(string sql) throws DatabaseError {
- var prepared = new Statement(this, sql);
- return prepared;
+ if (Db.Context.enable_sql_logging) {
+ debug(sql);
+ }
+ return new Statement(this, sql);
}
/** {@inheritDoc} */
@@ -136,11 +138,10 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
/** {@inheritDoc} */
public void exec(string sql, GLib.Cancellable? cancellable = null)
throws GLib.Error {
+ check_cancelled("Connection.exec", cancellable);
if (Db.Context.enable_sql_logging) {
- debug("exec:\n\t%s", sql);
+ debug(sql);
}
-
- check_cancelled("Connection.exec", cancellable);
var timer = new GLib.Timer();
throw_on_error("Connection.exec_file", this.db.exec(sql), sql);
check_elapsed("Query \"%s\"".printf(sql), timer);
@@ -150,6 +151,9 @@ public class Geary.Db.DatabaseConnection : Context, Connection {
public void exec_file(GLib.File file, GLib.Cancellable? cancellable = null)
throws GLib.Error {
check_cancelled("Connection.exec_file", cancellable);
+ if (Db.Context.enable_sql_logging) {
+ debug(file.get_path());
+ }
string sql;
FileUtils.get_contents(file.get_path(), out sql);
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index 8c40c475..14dc71a9 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -8,8 +8,12 @@ public class Geary.Db.Result : Geary.Db.Context {
public bool finished { get; private set; default = false; }
+ /** The statement this result was generated from. */
public Statement statement { get; private set; }
+ /** The current row represented by this result. */
+ public uint64 row { get; private set; default = 0; }
+
/** {@inheritDoc} */
public override Logging.Source? logging_parent {
get { return this.statement; }
@@ -39,7 +43,8 @@ public class Geary.Db.Result : Geary.Db.Context {
public bool next(Cancellable? cancellable = null) throws Error {
check_cancelled("Result.next", cancellable);
- if (!finished) {
+ if (!this.finished) {
+ this.row++;
var timer = new GLib.Timer();
this.finished = throw_on_error(
"Result.next", statement.stmt.step(), statement.sql
@@ -298,7 +303,12 @@ public class Geary.Db.Result : Geary.Db.Context {
/** {@inheritDoc} */
public override Logging.State to_logging_state() {
- return new Logging.State(this, this.finished ? "finished" : "not finished");
+ return new Logging.State(
+ this,
+ "%llu, %s",
+ this.row,
+ this.finished ? "finished" : "!finished"
+ );
}
internal override Result? get_result() {
@@ -306,16 +316,9 @@ public class Geary.Db.Result : Geary.Db.Context {
}
[PrintfFormat]
- private void log_result(string fmt, ...) {
- if (Db.Context.enable_sql_logging) {
- Statement? stmt = get_statement();
- if (stmt != null) {
- debug("%s\n\t<%s>",
- fmt.vprintf(va_list()),
- (stmt != null) ? "%.100s".printf(stmt.sql) : "no sql");
- } else {
- debug(fmt.vprintf(va_list()));
- }
+ private inline void log_result(string fmt, ...) {
+ if (Db.Context.enable_result_logging) {
+ debug(fmt.vprintf(va_list()));
}
}
--
2.29.2

View File

@ -0,0 +1,354 @@
From a30afb9e861450f042fd0d1e52ab6b965a247065 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Wed, 14 Oct 2020 14:44:26 +0200
Subject: [PATCH 7/8] account-editor: replace remove confirm view with dialog
---
po/POTFILES.in | 1 -
.../accounts/accounts-editor-edit-pane.vala | 23 ++-
.../accounts/accounts-editor-remove-pane.vala | 74 --------
src/client/meson.build | 1 -
ui/accounts_editor_remove_pane.ui | 159 ------------------
ui/geary.css | 8 +
ui/org.gnome.Geary.gresource.xml | 1 -
7 files changed, 30 insertions(+), 237 deletions(-)
delete mode 100644 src/client/accounts/accounts-editor-remove-pane.vala
delete mode 100644 ui/accounts_editor_remove_pane.ui
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cd8b339d..d9dcb0b7 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -9,7 +9,6 @@ src/client/accounts/accounts-editor.vala
src/client/accounts/accounts-editor-add-pane.vala
src/client/accounts/accounts-editor-edit-pane.vala
src/client/accounts/accounts-editor-list-pane.vala
-src/client/accounts/accounts-editor-remove-pane.vala
src/client/accounts/accounts-editor-row.vala
src/client/accounts/accounts-editor-servers-pane.vala
src/client/accounts/accounts-manager.vala
diff --git a/src/client/accounts/accounts-editor-edit-pane.vala b/src/client/accounts/accounts-editor-edit-pane.vala
index 5b944333..405dc7ba 100644
--- a/src/client/accounts/accounts-editor-edit-pane.vala
+++ b/src/client/accounts/accounts-editor-edit-pane.vala
@@ -217,7 +217,28 @@ internal class Accounts.EditorEditPane :
[GtkCallback]
private void on_remove_account_clicked() {
if (!this.editor.accounts.is_goa_account(account)) {
- this.editor.push(new EditorRemovePane(this.editor, this.account));
+ var button = new Gtk.Button.with_mnemonic(_("Remove Account"));
+ button.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION);
+ button.show();
+
+ var dialog = new Gtk.MessageDialog(this.editor,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.NONE,
+ _("Remove Account: %s"),
+ account.primary_mailbox.address);
+ dialog.secondary_text = _("This will remove it from Geary and delete locally cached email data from your computer. Nothing will be deleted from your service provider.");
+
+ dialog.add_button (_("_Cancel"), Gtk.ResponseType.CANCEL);
+ dialog.add_action_widget(button, Gtk.ResponseType.ACCEPT);
+
+ dialog.response.connect((response_id) => {
+ if (response_id == Gtk.ResponseType.ACCEPT)
+ this.editor.remove_account(this.account);
+
+ dialog.destroy();
+ });
+ dialog.show();
}
}
diff --git a/src/client/accounts/accounts-editor-remove-pane.vala b/src/client/accounts/accounts-editor-remove-pane.vala
deleted file mode 100644
index 2a6844cd..00000000
--- a/src/client/accounts/accounts-editor-remove-pane.vala
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2018-2019 Michael Gratton <mike@vee.net>
- *
- * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later). See the COPYING file in this distribution.
- */
-
-/**
- * An account editor pane for removing an account from the client.
- */
-[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_remove_pane.ui")]
-internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
-
-
- /** {@inheritDoc} */
- internal weak Accounts.Editor editor { get; set; }
-
- /** {@inheritDoc} */
- internal Geary.AccountInformation account { get ; protected set; }
-
- /** {@inheritDoc} */
- internal Gtk.Widget initial_widget {
- get { return this.remove_button; }
- }
-
- /** {@inheritDoc} */
- internal bool is_operation_running { get; protected set; default = false; }
-
- /** {@inheritDoc} */
- internal GLib.Cancellable? op_cancellable {
- get; protected set; default = null;
- }
-
- [GtkChild]
- private Gtk.HeaderBar header;
-
- [GtkChild]
- private Gtk.Label warning_label;
-
- [GtkChild]
- private Gtk.Button remove_button;
-
-
- public EditorRemovePane(Editor editor, Geary.AccountInformation account) {
- this.editor = editor;
- this.account = account;
-
- this.warning_label.set_text(
- this.warning_label.get_text().printf(account.display_name)
- );
-
- connect_account_signals();
- }
-
- ~EditorRemovePane() {
- disconnect_account_signals();
- }
-
- /** {@inheritDoc} */
- internal Gtk.HeaderBar get_header() {
- return this.header;
- }
-
- [GtkCallback]
- private void on_remove_button_clicked() {
- this.editor.remove_account(this.account);
- }
-
- [GtkCallback]
- private void on_back_button_clicked() {
- this.editor.pop();
- }
-
-}
diff --git a/src/client/meson.build b/src/client/meson.build
index 4efadc6d..6514adca 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -38,7 +38,6 @@ client_vala_sources = files(
'accounts/accounts-editor-add-pane.vala',
'accounts/accounts-editor-edit-pane.vala',
'accounts/accounts-editor-list-pane.vala',
- 'accounts/accounts-editor-remove-pane.vala',
'accounts/accounts-editor-row.vala',
'accounts/accounts-editor-servers-pane.vala',
'accounts/accounts-signature-web-view.vala',
diff --git a/ui/accounts_editor_remove_pane.ui b/ui/accounts_editor_remove_pane.ui
deleted file mode 100644
index 7edf4c13..00000000
--- a/ui/accounts_editor_remove_pane.ui
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
-<interface>
- <requires lib="gtk+" version="3.20"/>
- <template class="AccountsEditorRemovePane" parent="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <child>
- <object class="HdyClamp">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin">24</property>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">start</property>
- <property name="vexpand">True</property>
- <property name="row_spacing">32</property>
- <child>
- <object class="GtkButtonBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="remove_button">
- <property name="label" translatable="yes" comments="This is the remove account button in the account settings.">Remove Account</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Remove this account from Geary</property>
- <signal name="clicked" handler="on_remove_button_clicked" swapped="no"/>
- <style>
- <class name="destructive-action"/>
- </style>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <style>
- <class name="geary-settings"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">18</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">dialog-warning-symbolic</property>
- <property name="icon_size">6</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- <property name="height">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="warning_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="vexpand">True</property>
- <property name="label" translatable="yes" comments="This title is shown to users when confirming if they want to remove an account. The string substitution is replaced with the account's name.">Confirm removing: %s</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- <style>
- <class name="title"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Removing an account will remove it from Geary and delete locally cached email data from your computer, but not from your service provider.</property>
- <property name="wrap">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <style>
- <class name="geary-accounts-editor-pane-content"/>
- </style>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <style>
- <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" translatable="yes">Remove account</property>
- <property name="subtitle" translatable="yes">Account name</property>
- <property name="show_close_button">True</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>
- </object>
-</interface>
diff --git a/ui/geary.css b/ui/geary.css
index ca3cf640..3877038f 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -334,6 +334,14 @@ popover.geary-editor > grid > button.geary-setting-remove {
margin-top: 12px;
}
+dialog.geary-remove-confirm .dialog-vbox {
+ margin: 12px;
+}
+
+dialog.geary-remove-confirm .dialog-action-box {
+ margin: 6px;
+}
+
/* FolderList.Tree */
treeview.sidebar:drop(active).after,
diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml
index 0b9e900f..efdc87d6 100644
--- a/ui/org.gnome.Geary.gresource.xml
+++ b/ui/org.gnome.Geary.gresource.xml
@@ -5,7 +5,6 @@
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_add_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_edit_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_list_pane.ui</file>
- <file compressed="true" preprocess="xml-stripblanks">accounts_editor_remove_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_servers_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">application-main-window.ui</file>
<file compressed="true" preprocess="xml-stripblanks">certificate_warning_dialog.glade</file>
--
2.29.2

View File

@ -0,0 +1,262 @@
From 915a38faca33caf04cab2398a52d743dea554359 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 11 Sep 2020 00:00:02 +1000
Subject: [PATCH 008/124] Geary.ImapDb.Account: Slice up search table
population work better
Although populating the search table had been broken up into batches
of 50 email, it was still search for and loading every single message
id in both the MessageTable and MessageSearchTable, doing a manual
join, and then updating the batch, for *each* batch, and in a RW
transaction.
Break this up so that the ids are loaded and joined only once, the
queries happens in a RO transaction, the manual join happens in a side
thread, leaving each RW transaction only having to load the messages
and update the search index for up to 50 messages.
---
src/engine/imap-db/imap-db-account.vala | 205 ++++++++++++++----------
1 file changed, 120 insertions(+), 85 deletions(-)
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index 99244dc2..54522b90 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -955,8 +955,78 @@ private class Geary.ImapDB.Account : BaseObject {
public async void populate_search_table(Cancellable? cancellable) {
debug("%s: Populating search table", account_information.id);
+ // Since all queries involved can be quite extensive and this
+ // is not a time-critical operation, split them up
+
+ var search_ids = new Gee.HashSet<int64?>(
+ Collection.int64_hash_func,
+ Collection.int64_equal_func
+ );
+ var message_ids = new Gee.HashSet<int64?>(
+ Collection.int64_hash_func,
+ Collection.int64_equal_func
+ );
+ var unindexed_message_ids = new Gee.HashSet<int64?>(
+ Collection.int64_hash_func,
+ Collection.int64_equal_func
+ );
+
try {
- while (!yield populate_search_table_batch_async(50, cancellable)) {
+ yield this.db.exec_transaction_async(
+ RO,
+ (cx, cancellable) => {
+ // Embedding a SELECT within a SELECT is painfully slow
+ // with SQLite, and a LEFT OUTER JOIN will still take in
+ // the order of seconds, so manually perform the operation
+
+ var result = cx.prepare(
+ "SELECT docid FROM MessageSearchTable"
+ ).exec(cancellable);
+ while (!result.finished) {
+ search_ids.add(result.rowid_at(0));
+ result.next(cancellable);
+ }
+
+ var stmt = cx.prepare(
+ "SELECT id FROM MessageTable WHERE (fields & ?) = ?"
+ );
+ stmt.bind_uint(0, Geary.ImapDB.Folder.REQUIRED_FTS_FIELDS);
+ stmt.bind_uint(1, Geary.ImapDB.Folder.REQUIRED_FTS_FIELDS);
+ result = stmt.exec(cancellable);
+ while (!result.finished) {
+ message_ids.add(result.rowid_at(0));
+ result.next(cancellable);
+ }
+
+ return DONE;
+ },
+ cancellable
+ );
+
+ // Run this in a separate thread since it could be quite a
+ // substantial process for large accounts
+ yield Nonblocking.Concurrent.global.schedule_async(
+ () => {
+ foreach (int64 message_id in message_ids) {
+ if (!search_ids.contains(message_id)) {
+ unindexed_message_ids.add(message_id);
+ }
+ }
+ },
+ cancellable
+ );
+
+ debug("%s: Found %d missing messages to populate",
+ this.account_information.id,
+ unindexed_message_ids.size
+ );
+
+ // Do the actual updating in batches since these require
+ // RW transactions
+ while (!unindexed_message_ids.is_empty) {
+ yield populate_search_table_batch_async(
+ 50, unindexed_message_ids, cancellable
+ );
// With multiple accounts, meaning multiple background threads
// doing such CPU- and disk-heavy work, this process can cause
// the main thread to slow to a crawl. This delay means the
@@ -965,105 +1035,70 @@ private class Geary.ImapDB.Account : BaseObject {
yield Geary.Scheduler.sleep_ms_async(50);
}
} catch (Error e) {
- debug("Error populating %s search table: %s", account_information.id, e.message);
+ debug("%s: Error populating search table: %s", account_information.id, e.message);
}
debug("%s: Done populating search table", account_information.id);
}
- private static Gee.HashSet<int64?> do_build_rowid_set(Db.Result result, Cancellable? cancellable)
- throws Error {
- Gee.HashSet<int64?> rowid_set = new Gee.HashSet<int64?>(Collection.int64_hash_func,
- Collection.int64_equal_func);
- while (!result.finished) {
- rowid_set.add(result.rowid_at(0));
- result.next(cancellable);
- }
-
- return rowid_set;
- }
-
- private async bool populate_search_table_batch_async(int limit, Cancellable? cancellable)
- throws Error {
+ private async void populate_search_table_batch_async(
+ int limit,
+ Gee.HashSet<int64?> unindexed_message_ids,
+ GLib.Cancellable? cancellable
+ ) throws GLib.Error {
check_open();
- debug("%s: Searching for up to %d missing indexed messages...", account_information.id,
- limit);
-
- int count = 0, total_unindexed = 0;
- yield db.exec_transaction_async(Db.TransactionType.RW, (cx, cancellable) => {
- // Embedding a SELECT within a SELECT is painfully slow
- // with SQLite, and a LEFT OUTER JOIN will still take in
- // the order of seconds, so manually perform the operation
- Db.Statement stmt = cx.prepare("""
- SELECT docid FROM MessageSearchTable
- """);
- Gee.HashSet<int64?> search_ids = do_build_rowid_set(stmt.exec(cancellable), cancellable);
-
- stmt = cx.prepare("""
- SELECT id FROM MessageTable WHERE (fields & ?) = ?
- """);
- stmt.bind_uint(0, Geary.ImapDB.Folder.REQUIRED_FTS_FIELDS);
- stmt.bind_uint(1, Geary.ImapDB.Folder.REQUIRED_FTS_FIELDS);
- Gee.HashSet<int64?> message_ids = do_build_rowid_set(stmt.exec(cancellable), cancellable);
-
- // This is hard to calculate correctly without doing a
- // join (which we should above, but is currently too
- // slow), and if we do get it wrong the progress monitor
- // will crash and burn, so just something too big to fail
- // for now. See Bug 776383.
- total_unindexed = message_ids.size;
-
- // chaff out any MessageTable entries not present in the MessageSearchTable ... since
- // we're given a limit, stuff messages req'ing search into separate set and stop when limit
- // reached
- Gee.HashSet<int64?> unindexed_message_ids = new Gee.HashSet<int64?>(Collection.int64_hash_func,
- Collection.int64_equal_func);
- foreach (int64 message_id in message_ids) {
- if (search_ids.contains(message_id))
- continue;
-
- unindexed_message_ids.add(message_id);
- if (unindexed_message_ids.size >= limit)
- break;
- }
-
- // For all remaining MessageTable rowid's, generate search table entry
- foreach (int64 message_id in unindexed_message_ids) {
- try {
- Geary.Email.Field search_fields = Geary.Email.REQUIRED_FOR_MESSAGE |
- Geary.Email.Field.ORIGINATORS | Geary.Email.Field.RECEIVERS |
- Geary.Email.Field.SUBJECT;
+ uint count = 0;
+ var iter = unindexed_message_ids.iterator();
+ yield this.db.exec_transaction_async(
+ RW,
+ (cx, cancellable) => {
+ while (iter.has_next() && count < limit) {
+ iter.next();
+ int64 message_id = iter.get();
+ try {
+ Email.Field search_fields = (
+ Email.REQUIRED_FOR_MESSAGE |
+ Email.Field.ORIGINATORS |
+ Email.Field.RECEIVERS |
+ Email.Field.SUBJECT
+ );
- Geary.Email.Field db_fields;
- MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
- cx, message_id, search_fields, out db_fields, cancellable);
- Geary.Email email = row.to_email(new Geary.ImapDB.EmailIdentifier(message_id, null));
- Attachment.add_attachments(
- cx, this.db.attachments_path, email, message_id, cancellable
- );
+ Email.Field db_fields;
+ MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
+ cx, message_id, search_fields, out db_fields, cancellable
+ );
+ Email email = row.to_email(
+ new Geary.ImapDB.EmailIdentifier(message_id, null)
+ );
+ Attachment.add_attachments(
+ cx, this.db.attachments_path, email, message_id, cancellable
+ );
+ Geary.ImapDB.Folder.do_add_email_to_search_table(
+ cx, message_id, email, cancellable
+ );
+ } catch (GLib.Error e) {
+ // This is a somewhat serious issue since we rely on
+ // there always being a row in the search table for
+ // every message.
+ warning(
+ "Error populating message %s for indexing: %s",
+ message_id.to_string(),
+ e.message
+ );
+ }
- Geary.ImapDB.Folder.do_add_email_to_search_table(cx, message_id, email, cancellable);
- } catch (Error e) {
- // This is a somewhat serious issue since we rely on
- // there always being a row in the search table for
- // every message.
- warning("Error adding message %s to the search table: %s", message_id.to_string(),
- e.message);
+ iter.remove();
+ ++count;
}
- ++count;
- }
-
- return Db.TransactionOutcome.DONE;
+ return COMMIT;
}, cancellable);
if (count > 0) {
- debug("%s: Found %d/%d missing indexed messages, %d remaining...",
- account_information.id, count, limit, total_unindexed);
+ debug("%s: Populated %u missing indexed messages...",
+ account_information.id, count);
}
-
- return (count < limit);
}
//
--
2.29.2

View File

@ -0,0 +1,41 @@
From 17afbca1727cc7780358239c7cafc8de1742bb0c Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 20 Oct 2020 12:20:45 +0200
Subject: [PATCH 8/8] account-editor: don't show close button for edit/servers
pane
This makes it consistent with the other panes.
---
ui/accounts_editor_edit_pane.ui | 2 +-
ui/accounts_editor_servers_pane.ui | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ui/accounts_editor_edit_pane.ui b/ui/accounts_editor_edit_pane.ui
index 33b4a9e8..ce9bcb9a 100644
--- a/ui/accounts_editor_edit_pane.ui
+++ b/ui/accounts_editor_edit_pane.ui
@@ -8,7 +8,7 @@
<property name="title" translatable="yes">Edit Account</property>
<property name="subtitle" translatable="yes">Account Name</property>
<property name="has_subtitle">False</property>
- <property name="show_close_button">True</property>
+ <property name="show_close_button">False</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
diff --git a/ui/accounts_editor_servers_pane.ui b/ui/accounts_editor_servers_pane.ui
index a01eba5b..77ba674e 100644
--- a/ui/accounts_editor_servers_pane.ui
+++ b/ui/accounts_editor_servers_pane.ui
@@ -7,7 +7,7 @@
<property name="can_focus">False</property>
<property name="title" translatable="yes">Server Settings</property>
<property name="subtitle" translatable="yes">Account Name</property>
- <property name="show_close_button">True</property>
+ <property name="show_close_button">False</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
--
2.29.2

View File

@ -0,0 +1,34 @@
From 3d8b86dd4e46939dc1a839873a2d049d650d0491 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 12 Sep 2020 12:02:49 +1000
Subject: [PATCH 009/124] Geary.ImapDB.Folder: Drop create/merge batch size
down
We've had reports on Matrix that existing batch transactions in
`create_or_merge_email_async` are taking up to 30s to run, which is
getting uncomfortably close to the 60s timeout.
Drop the batch size down from 25 to 10 to reduce the chance that this
will eventually fail with a "database is locked" error.
See #921 for context.
---
src/engine/imap-db/imap-db-folder.vala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index 4b015cd1..2c2d4015 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -40,7 +40,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
private const int LIST_EMAIL_METADATA_COUNT = 100;
private const int LIST_EMAIL_FIELDS_CHUNK_COUNT = 500;
private const int REMOVE_COMPLETE_LOCATIONS_CHUNK_COUNT = 500;
- private const int CREATE_MERGE_EMAIL_CHUNK_COUNT = 25;
+ private const int CREATE_MERGE_EMAIL_CHUNK_COUNT = 10;
private const int OLD_MSG_DETACH_BATCH_SIZE = 1000;
// When old messages beyond the period set in the account preferences are removed this number
--
2.29.2

View File

@ -0,0 +1,405 @@
From 56b77cf0d7595c1ccd0846d410a16cb5d5c540b8 Mon Sep 17 00:00:00 2001
From: Fabio Tomat <f.t.public@gmail.com>
Date: Mon, 14 Sep 2020 19:32:40 +0000
Subject: [PATCH 010/124] Update Friulian translation
---
po/fur.po | 103 +++++++++++++++++++++++++++++-------------------------
1 file changed, 55 insertions(+), 48 deletions(-)
diff --git a/po/fur.po b/po/fur.po
index f680a065..fd5e3639 100644
--- a/po/fur.po
+++ b/po/fur.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-08-29 01:59+0000\n"
-"PO-Revision-Date: 2020-09-08 16:21+0200\n"
+"POT-Creation-Date: 2020-09-13 04:23+0000\n"
+"PO-Revision-Date: 2020-09-14 21:31+0200\n"
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
@@ -232,6 +232,8 @@ msgid ""
"Enables shortcuts for email actions that do not require pressing <Ctrl> to "
"emulate those used by Gmail."
msgstr ""
+"Al abilite lis scurtis pes azions e-mail che no àn bisugne de pression di "
+"<Ctrl> par emulâ chês dopradis di Gmail."
#: desktop/org.gnome.Geary.gschema.xml:82
msgid "Languages that shall be used in the spell checker"
@@ -242,16 +244,23 @@ msgid ""
"A list of POSIX locales, with the empty list disabling spell checking and "
"the null list using desktop languages by default."
msgstr ""
+"Une liste di localizazions POSIX, cuntune liste vueide si disabilite il "
+"control ortografic e cuntune liste nule si dopre lis lenghis predefinidis "
+"dal scritori."
#: desktop/org.gnome.Geary.gschema.xml:90
msgid "Languages that are displayed in the spell checker popover"
msgstr ""
+"Lis lenghis che a vegnin mostradis intal ricuadri a comparse dal control "
+"ortografic"
#: desktop/org.gnome.Geary.gschema.xml:91
msgid ""
"List of languages that are always displayed in the popover of the spell "
"checker."
msgstr ""
+"Liste di lenghis che a vegnin simpri mostradis intal ricuadri a comparse dal "
+"control ortografic."
#: desktop/org.gnome.Geary.gschema.xml:96
msgid "Notify of new mail at startup"
@@ -285,6 +294,7 @@ msgstr ""
msgid ""
"Acceptable values are “exact”, “conservative”, “aggressive”, and “horizon”."
msgstr ""
+"I valôrs ametûts a son “exact”, “conservative”, “aggressive”, e “horizon”."
#: desktop/org.gnome.Geary.gschema.xml:120
msgid "Zoom of conversation viewer"
@@ -304,13 +314,15 @@ msgstr "La ultime dimension regjistrade dal barcon dal redatôr distacât."
#: desktop/org.gnome.Geary.gschema.xml:132
msgid "Undo sending email delay"
-msgstr ""
+msgstr "Anule ritart di spedizion e-mail"
#: desktop/org.gnome.Geary.gschema.xml:133
msgid ""
"The number of seconds to wait before sending an email. Set to zero or less "
"to disable."
msgstr ""
+"Il numar di seconts di spietâ prime di inviâ une e-mail. Met a zero o mancul "
+"par disabilitâ."
#: desktop/org.gnome.Geary.gschema.xml:139
msgid "Brief notification display time"
@@ -320,7 +332,7 @@ msgstr ""
msgid ""
"The length of time in seconds for which brief notifications should be "
"displayed."
-msgstr ""
+msgstr "La lungjece temporâl in seconts che si à di mostrâ lis notifichis."
#: desktop/org.gnome.Geary.gschema.xml:146
msgid "List of optional plugins"
@@ -332,12 +344,13 @@ msgstr "I plugin listâts achì a vignaran cjariâts al inviament."
#: desktop/org.gnome.Geary.gschema.xml:152
msgid "Whether we migrated the old settings"
-msgstr ""
+msgstr "Indiche se o ven migrât lis impostazions vecjis"
#: desktop/org.gnome.Geary.gschema.xml:153
msgid ""
"False to check for the old “org.yorba.geary”-schema and copy its values."
msgstr ""
+"Fals par controlâ il vecjo scheme “org.yorba.geary” e copiâ i siei valôrs."
#. Translators: In-app notification label, when
#. the app had a problem pinning an otherwise
@@ -772,11 +785,11 @@ msgstr "Salve pueste inviade sul servidôr"
#: src/client/accounts/accounts-editor-servers-pane.vala:961
#, c-format
msgid "%s using OAuth2"
-msgstr ""
+msgstr "%s doprant OAuth2"
#: src/client/accounts/accounts-editor-servers-pane.vala:971
msgid "Use receiving server login"
-msgstr ""
+msgstr "Dopre acès dal servidôr di ricezion"
#. Translators: File name used in save chooser when saving
#. attachments that do not otherwise have a name.
@@ -822,12 +835,12 @@ msgstr "Visite il sît web di Geary"
#. / Command line option
#: src/client/application/application-client.vala:97
msgid "Print debug logging"
-msgstr ""
+msgstr "Stampe i regjistris di debug"
#. / Command line option
#: src/client/application/application-client.vala:100
msgid "Start with the main window hidden (deprecated)"
-msgstr ""
+msgstr "Partìs cul barcon principâl platât (deplorât)"
#. / Command line option
#: src/client/application/application-client.vala:103
@@ -960,13 +973,13 @@ msgstr "Fabio Tomat <f.t.public@gmail.com>, 2020"
#. / Warning printed to the console when a deprecated
#. / command line option is used.
-#: src/client/application/application-client.vala:1047
+#: src/client/application/application-client.vala:1045
msgid "The `--hidden` option is deprecated and will be removed in the future."
msgstr "La opzion `--hidden` e je deplorade e un doman e vignarà gjavade."
#. / Command line warning, string substitution
#. / is the given argument
-#: src/client/application/application-client.vala:1080
+#: src/client/application/application-client.vala:1078
#, c-format
msgid "Unrecognised program argument: “%s”"
msgstr "Argoment di program no ricognossût: “%s”"
@@ -1322,7 +1335,7 @@ msgstr "Salve come"
#: src/client/components/components-inspector.vala:230
#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
-#: ui/accounts_editor_servers_pane.ui:17
+#: ui/accounts_editor_servers_pane.ui:17 ui/composer-headerbar.ui:61
msgid "Cancel"
msgstr "Anule"
@@ -2208,10 +2221,8 @@ msgstr "Compile e invie modei di e-mail doprant un sfuei di calcul"
#. Translators: Info bar label for starting sending a mail
#. merge
#: src/client/plugin/mail-merge/mail-merge.vala:118
-#, fuzzy
-#| msgid "StartTLS"
msgid "Start"
-msgstr "StartTLS"
+msgstr "Scomence"
#. Translators: Info bar label for pausing sending a mail
#. merge
@@ -2379,21 +2390,21 @@ msgstr "%A"
#. / http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
#: src/client/util/util-date.vala:218
msgid "%a, %b %-e, %Y at %l:%M %P"
-msgstr ""
+msgstr "%a, %-e di %b dal %Y aes %l:%M %P"
#. / 24 hours format for the datetime that a message being
#. / replied to was received See
#. / http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
#: src/client/util/util-date.vala:224
msgid "%a, %b %-e, %Y at %H:%M"
-msgstr ""
+msgstr "%a, %-e di %b dal %Y aes %H:%M"
#. / Format for the datetime that a message being replied to
#. / was received See
#. / http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
#: src/client/util/util-date.vala:230
msgid "%a, %b %-e, %Y at %X"
-msgstr ""
+msgstr "%a, %-e di %b dal %Y aes %X"
#. Translators: Label used when an email has a missing or
#. an empty subject
@@ -2426,7 +2437,7 @@ msgstr[1] "%s e altris %d"
#: src/client/util/util-email.vala:193
#, c-format
msgid "On %1$s, %2$s wrote:"
-msgstr ""
+msgstr "Ai %1$s, %2$s al/e à scrit:"
#. / The quoted header for a message being replied to (in case the date is not known).
#. / %s will be replaced by the original sender.
@@ -2721,7 +2732,6 @@ msgstr ""
#. put the most common localized name to the front for the
#. default. English names do not need to be included.
#: src/engine/imap-engine/imap-engine-generic-account.vala:1030
-#, fuzzy
msgid "Trash | Rubbish | Rubbish Bin"
msgstr "Scovacere | Refudum | Bidon"
@@ -2811,7 +2821,7 @@ msgstr "Account"
#: ui/accounts_editor_list_pane.ui:62
msgid "To get started, select an email provider below."
-msgstr ""
+msgstr "Par scomençâ, selezione un furnidôr di e-mail chi sot."
#: ui/accounts_editor_list_pane.ui:75
msgid "Welcome to Geary"
@@ -2855,27 +2865,27 @@ msgstr "_No sta fidâti di chest servidôr"
#: ui/composer-editor.ui:100
msgid "Bold text"
-msgstr ""
+msgstr "Test neret"
#: ui/composer-editor.ui:124
msgid "Italic text"
-msgstr ""
+msgstr "Test corsîf"
#: ui/composer-editor.ui:148
msgid "Underline text"
-msgstr ""
+msgstr "Test sotlineât"
#: ui/composer-editor.ui:172
msgid "Strikethrough text"
-msgstr ""
+msgstr "Test stricât"
#: ui/composer-editor.ui:205
msgid "Insert bulleted list"
-msgstr ""
+msgstr "Inserìs liste pontade"
#: ui/composer-editor.ui:229
msgid "Insert numbered list"
-msgstr ""
+msgstr "Inserìs liste numerade"
#: ui/composer-editor.ui:262
msgid "Indent or quote text"
@@ -2887,7 +2897,7 @@ msgstr ""
#: ui/composer-editor.ui:315
msgid "Remove text formatting"
-msgstr ""
+msgstr "Gjave formatazion dal test"
#: ui/composer-editor.ui:334
msgid "Change font type"
@@ -2915,7 +2925,7 @@ msgstr "Cambie dimension dal caratar"
#: ui/composer-editor.ui:447
msgid "Insert or update text link"
-msgstr ""
+msgstr "Inserìs o inzorne il colegament dal test"
#: ui/composer-editor.ui:471
msgid "Insert an image"
@@ -3034,7 +3044,7 @@ msgstr "Salve e siere"
#. Note that this button and the Update button will never be shown at the same time to the user.
#: ui/composer-link-popover.ui:42
msgid "Insert the new link with this URL"
-msgstr ""
+msgstr "Inserìs il gnûf colegament cun chest URL"
#: ui/composer-link-popover.ui:43
msgid "Add"
@@ -3047,7 +3057,7 @@ msgstr "URL colegament"
#. Note that this button and the Insert button will never be shown at the same time to the user.
#: ui/composer-link-popover.ui:61
msgid "Update this links URL"
-msgstr ""
+msgstr "Inzorne chest URL dal link"
#: ui/composer-link-popover.ui:62
msgid "Update"
@@ -3127,7 +3137,7 @@ msgstr "Detais:"
#. Tooltip for problem report button
#: ui/components-inspector.ui:19 ui/problem-details-dialog.ui:24
msgid "Search for matching log entries"
-msgstr ""
+msgstr "Cîr vôs di regjistri corispondentis"
#. Tooltip for inspector button
#: ui/components-inspector.ui:35
@@ -3143,7 +3153,7 @@ msgstr ""
#. Tooltip for problem report button
#: ui/components-inspector.ui:81 ui/problem-details-dialog.ui:51
msgid "Save logs entries and details"
-msgstr ""
+msgstr "Salve vôs dai regjistris e detais"
#. Tooltip for inspector button
#. Tooltip for problem report button
@@ -3182,10 +3192,8 @@ msgstr "Cjarie simpri lis imagjins rimotis"
#. Title label on contact popover
#: ui/conversation-contact-popover.ui:264
-#, fuzzy
-#| msgid "Email address"
msgid "Deceptive email address"
-msgstr "Direzion e-mail"
+msgstr "Direzion e-mail ingjanose"
#. Contact popover label
#: ui/conversation-contact-popover.ui:294
@@ -3245,7 +3253,6 @@ msgstr "Sposte messaç te _Scovacere"
#. Translators: Menu item to delete a single, specific message
#: ui/conversation-email-menus.ui:57
-#| msgid "Autoselect next message"
msgid "_Delete message…"
msgstr "_Elimine messaç…"
@@ -3256,23 +3263,23 @@ msgstr "_Viôt sorzint"
#: ui/conversation-message-link-popover.ui:54
msgid "But actually goes to:"
-msgstr ""
+msgstr "Ma invezit al va su:"
#: ui/conversation-message-link-popover.ui:84
msgid "The link appears to go to:"
-msgstr ""
+msgstr "Al semee che il colegament al ledi su:"
#: ui/conversation-message-link-popover.ui:96
msgid "Deceptive link found"
-msgstr ""
+msgstr "Colegament ingjanôs cjatât"
#: ui/conversation-message-link-popover.ui:111
msgid "The email sender may be leading you to the wrong web site."
-msgstr ""
+msgstr "Il mitent de e-mail al podarès puartâti su sîts web sbaliâts."
#: ui/conversation-message-link-popover.ui:124
msgid "If unsure, contact the sender and ask before continuing."
-msgstr ""
+msgstr "Se no si è sigûrs, contatâ il mitent e domandâ prime di continuâ."
#: ui/conversation-message-menus.ui:7
msgid "_Open Link"
@@ -3611,27 +3618,27 @@ msgstr "Modifiche cun test formatât"
#: ui/gtk/help-overlay.ui:468
msgctxt "shortcut window"
msgid "Paste without formatting"
-msgstr ""
+msgstr "Tache cence formatazion"
#: ui/gtk/help-overlay.ui:475
msgctxt "shortcut window"
msgid "Bold text"
-msgstr ""
+msgstr "Met test in neret"
#: ui/gtk/help-overlay.ui:482
msgctxt "shortcut window"
msgid "Italicize text"
-msgstr ""
+msgstr "Met test in corsîf"
#: ui/gtk/help-overlay.ui:489
msgctxt "shortcut window"
msgid "Underline text"
-msgstr ""
+msgstr "Sotlinie il test"
#: ui/gtk/help-overlay.ui:496
msgctxt "shortcut window"
msgid "Strike text"
-msgstr ""
+msgstr "Striche il test"
#: ui/gtk/help-overlay.ui:503
msgctxt "shortcut window"
@@ -3711,7 +3718,7 @@ msgstr "_Autentiche"
#: ui/upgrade_dialog.glade:60
msgid "Geary update in progress…"
-msgstr ""
+msgstr "Inzornament di Geary in vore…"
#~ msgid "mail-send"
#~ msgstr "mail-send"
--
2.29.2

View File

@ -0,0 +1,180 @@
From 4a4e6a6992fc1349fd532ad35b0c787992024162 Mon Sep 17 00:00:00 2001
From: Fabio Tomat <f.t.public@gmail.com>
Date: Tue, 15 Sep 2020 05:20:06 +0000
Subject: [PATCH 011/124] Update Friulian translation
---
po/fur.po | 46 +++++++++++++++++++++++-----------------------
1 file changed, 23 insertions(+), 23 deletions(-)
diff --git a/po/fur.po b/po/fur.po
index fd5e3639..1db111d0 100644
--- a/po/fur.po
+++ b/po/fur.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-09-13 04:23+0000\n"
-"PO-Revision-Date: 2020-09-14 21:31+0200\n"
+"POT-Creation-Date: 2020-09-14 19:33+0000\n"
+"PO-Revision-Date: 2020-09-15 07:19+0200\n"
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
@@ -288,7 +288,7 @@ msgstr "Vêr par scrivi lis e-mail in HTML; fals pal test sempliç."
#: desktop/org.gnome.Geary.gschema.xml:114
msgid "Advisory strategy for full-text searching"
-msgstr ""
+msgstr "Strategjie consultive pe ricercje di tescj complets"
#: desktop/org.gnome.Geary.gschema.xml:115
msgid ""
@@ -326,7 +326,7 @@ msgstr ""
#: desktop/org.gnome.Geary.gschema.xml:139
msgid "Brief notification display time"
-msgstr ""
+msgstr "Timp di visualizazion des notifichis"
#: desktop/org.gnome.Geary.gschema.xml:140
msgid ""
@@ -580,7 +580,7 @@ msgstr "Discjarie pueste"
#: src/client/accounts/accounts-editor-edit-pane.vala:822
#, c-format
msgid "Change download period back to: %s"
-msgstr ""
+msgstr "Tornâ al precedent periodi di discjariament: %s"
#: src/client/accounts/accounts-editor-edit-pane.vala:843
msgid "Everything"
@@ -850,18 +850,18 @@ msgstr "Abilite l'ispetôr WebKitGTK intes viodudis web"
#. / Command line option
#: src/client/application/application-client.vala:106
msgid "Log conversation monitoring"
-msgstr ""
+msgstr "Regjistre il monitorament des conversazions"
#. / Command line option
#: src/client/application/application-client.vala:109
msgid "Log IMAP network deserialization"
-msgstr ""
+msgstr "Regjistre la deserializazion de rêt IMAP"
#. / Command line option. "Normalization" can also be called
#. / "synchronization".
#: src/client/application/application-client.vala:113
msgid "Log folder normalization"
-msgstr ""
+msgstr "Regjistre la sincronizazion de cartele"
#. / Command line option
#: src/client/application/application-client.vala:116
@@ -873,7 +873,7 @@ msgstr "Regjistre ativitât di rêt IMAP"
#. / also be called the IMAP events queue.
#: src/client/application/application-client.vala:121
msgid "Log IMAP replay queue"
-msgstr ""
+msgstr "Regjistre la code di events IMAP"
#. / Command line option
#: src/client/application/application-client.vala:124
@@ -897,10 +897,8 @@ msgstr "Vierç un gnûf barcon"
#. / Command line option
#: src/client/application/application-client.vala:135
-#, fuzzy
-#| msgid "Failed to store certificate"
msgid "Revoke all pinned TLS server certificates"
-msgstr "No si è rivâts a archiviâ il certificât"
+msgstr "Revoche ducj i certificâts di servidôr TLS fissâts"
#. / Command line option
#: src/client/application/application-client.vala:138
@@ -2268,10 +2266,8 @@ msgid "Messaging Menu"
msgstr "Menù di messaç"
#: src/client/plugin/messaging-menu/messaging-menu.plugin.desktop.in:5
-#, fuzzy
-#| msgid "Show notifications for new mail"
msgid "Displays Unity Messaging Menu notifications for new email"
-msgstr "Mostre lis notifichis pe gnove pueste"
+msgstr "Al mostre lis notifichis di gnove pueste tal menù dai messaçs di Unity"
#: src/client/plugin/messaging-menu/messaging-menu.vala:79
#, c-format
@@ -2838,6 +2834,8 @@ msgid ""
"Removing an account will remove it from Geary and delete locally cached "
"email data from your computer, but not from your service provider."
msgstr ""
+"Gjavant un account si lu gjavarà di Geary e si eliminarà dal computer i dâts "
+"e-mail te cache locâl, ma no dal gjestôr/furnidôr dal servizi."
#: ui/accounts_editor_remove_pane.ui:122
msgid "Remove account"
@@ -2889,11 +2887,11 @@ msgstr "Inserìs liste numerade"
#: ui/composer-editor.ui:262
msgid "Indent or quote text"
-msgstr ""
+msgstr "Indentâ o citâ test"
#: ui/composer-editor.ui:286
msgid "Un-indent or unquote text"
-msgstr ""
+msgstr "Gjavâ la indintidure o la citazion dal test"
#: ui/composer-editor.ui:315
msgid "Remove text formatting"
@@ -3128,6 +3126,10 @@ msgid ""
"channels</a> or attach to a <a href=\"https://wiki.gnome.org/Apps/Geary/"
"ReportingABug\">new bug report</a>."
msgstr ""
+"Se il probleme al è grivi o al persist, salve e invie chescj detais a un dai "
+"<a href=\"https://wiki.gnome.org/Apps/Geary/Contact\">canâi di contat</a> o "
+"zonte suntune <a href=\"https://wiki.gnome.org/Apps/Geary/ReportingABug"
+"\">gnove segnalazion di erôr</a>."
#: ui/components-inspector-error-view.ui:47
msgid "Details:"
@@ -3142,12 +3144,12 @@ msgstr "Cîr vôs di regjistri corispondentis"
#. Tooltip for inspector button
#: ui/components-inspector.ui:35
msgid "Toggle appending new log entries"
-msgstr ""
+msgstr "Ativâ/disativâ la zonte di gnovis vôs di regjistri"
#. Tooltip for inspector button
#: ui/components-inspector.ui:55
msgid "Add a marker entry to the log"
-msgstr ""
+msgstr "Zonte une vôs-marcadôr al regjistri"
#. Tooltip for inspector button
#. Tooltip for problem report button
@@ -3423,7 +3425,7 @@ msgstr "Bute vie conversazions"
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:340
msgctxt "shortcut window"
msgid "Junk conversations"
-msgstr ""
+msgstr "Conversazions malvoludis"
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:347
msgctxt "shortcut window"
@@ -3526,11 +3528,9 @@ msgid "Select next/previous conversation"
msgstr "Selezione la prossime/precedente conversazion"
#: ui/gtk/help-overlay.ui:248
-#, fuzzy
-#| msgid "Autoselect next message"
msgctxt "shortcut window"
msgid "Focus next/previous message"
-msgstr "Selezione in automatic il prossim messaç"
+msgstr "Met a fûc (il stât atîf) il prossim/precedent messaç"
#: ui/gtk/help-overlay.ui:260
msgid "Single-key Shortcuts"
--
2.29.2

View File

@ -0,0 +1,34 @@
From acabdc9e73e7a6e23f14540ac2485783161eecc4 Mon Sep 17 00:00:00 2001
From: Fabio Tomat <f.t.public@gmail.com>
Date: Tue, 15 Sep 2020 05:52:24 +0000
Subject: [PATCH 012/124] Update Friulian translation
---
po/fur.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/po/fur.po b/po/fur.po
index 1db111d0..d13465ba 100644
--- a/po/fur.po
+++ b/po/fur.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: geary master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
"POT-Creation-Date: 2020-09-14 19:33+0000\n"
-"PO-Revision-Date: 2020-09-15 07:19+0200\n"
+"PO-Revision-Date: 2020-09-15 07:51+0200\n"
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
@@ -2491,7 +2491,7 @@ msgstr "Inviade"
#: src/client/util/util-i18n.vala:280
msgid "Starred"
-msgstr "Preferidis"
+msgstr "Preferide"
#: src/client/util/util-i18n.vala:283
msgid "Important"
--
2.29.2

View File

@ -0,0 +1,34 @@
From e957f0f8a7310950d36837b9945a09ad7a97e63c Mon Sep 17 00:00:00 2001
From: Fabio Tomat <f.t.public@gmail.com>
Date: Tue, 15 Sep 2020 06:56:54 +0000
Subject: [PATCH 013/124] Update Friulian translation
---
po/fur.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/po/fur.po b/po/fur.po
index d13465ba..b5e17eec 100644
--- a/po/fur.po
+++ b/po/fur.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: geary master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
"POT-Creation-Date: 2020-09-14 19:33+0000\n"
-"PO-Revision-Date: 2020-09-15 07:51+0200\n"
+"PO-Revision-Date: 2020-09-15 08:56+0200\n"
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
@@ -1181,7 +1181,7 @@ msgstr "Si lavore fûr rêt"
#: src/client/application/application-main-window.vala:542
msgid "You will not be able to send or receive email until re-connected."
msgstr ""
-"No tu podarâs inviâ o ricevi e-mail fintremai che no tu tornarâs a coneti."
+"No tu podarâs inviâ o ricevi e-mail fintremai che no tu ti tornarâs a coneti."
#. Translators: An info bar status label
#: src/client/application/application-main-window.vala:549
--
2.29.2

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,276 @@
From 5942e30377f7e47abe60236ceac100384bf74add Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Du=C5=A1an=20Kazik?= <prescott66@gmail.com>
Date: Mon, 21 Sep 2020 11:52:17 +0000
Subject: [PATCH 017/124] Update Slovak translation
(cherry picked from commit 2028c9dea3f1d8f6c5dcfdd44f5f1990121e0983)
---
po/sk.po | 71 ++++++++++++++++++++++++++------------------------------
1 file changed, 33 insertions(+), 38 deletions(-)
diff --git a/po/sk.po b/po/sk.po
index 8fc3e1f4..dc0138c2 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -10,8 +10,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary-0.4.1\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-08-29 01:59+0000\n"
-"PO-Revision-Date: 2020-09-16 13:28+0200\n"
+"POT-Creation-Date: 2020-09-19 08:49+0000\n"
+"PO-Revision-Date: 2020-09-21 13:51+0200\n"
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
"Language: sk\n"
@@ -212,6 +212,8 @@ msgstr "Automaticky vybrať ďalšiu správu"
#: desktop/org.gnome.Geary.gschema.xml:63
msgid "True if we should autoselect the next available conversation."
msgstr ""
+"Nastavte na True, ak by sme mali automaticky vybrať nasledujúci dostupný "
+"rozhovor."
#: desktop/org.gnome.Geary.gschema.xml:68
msgid "Display message previews"
@@ -219,7 +221,7 @@ msgstr "Zobraziť náhľady správ"
#: desktop/org.gnome.Geary.gschema.xml:69
msgid "True if we should display a short preview of each message."
-msgstr ""
+msgstr "Nastavte na True, ak by sme mali zobraziť krátky náhľad každej správy."
#: desktop/org.gnome.Geary.gschema.xml:74
msgid "Use single key shortcuts"
@@ -261,7 +263,7 @@ msgstr "Nastavením na True, bude oznamovaná nová pošta po spustení."
#: desktop/org.gnome.Geary.gschema.xml:102
msgid "Ask when opening an attachment"
-msgstr ""
+msgstr "Opýtať sa pri otváraní prílohy"
#: desktop/org.gnome.Geary.gschema.xml:103
msgid "True to ask when opening an attachment."
@@ -290,7 +292,7 @@ msgstr "Priblíženie prehliadača konverzácií"
#: desktop/org.gnome.Geary.gschema.xml:121
msgid "The zoom to apply on the conservation view."
-msgstr ""
+msgstr "Priblíženie, ktoré sa má použiť pre zobrazenie rozhovorov."
#: desktop/org.gnome.Geary.gschema.xml:126
msgid "Size of detached composer window"
@@ -298,7 +300,7 @@ msgstr "Veľkosť odpojeného okna tvorcu správ"
#: desktop/org.gnome.Geary.gschema.xml:127
msgid "The last recorded size of the detached composer window."
-msgstr ""
+msgstr "Posledná zaznamenaná veľkosť odpojeného okna tvorcu správ."
#: desktop/org.gnome.Geary.gschema.xml:132
msgid "Undo sending email delay"
@@ -314,13 +316,15 @@ msgstr ""
#: desktop/org.gnome.Geary.gschema.xml:139
msgid "Brief notification display time"
-msgstr ""
+msgstr "Doba zobrazenia stručného oznámenia"
#: desktop/org.gnome.Geary.gschema.xml:140
msgid ""
"The length of time in seconds for which brief notifications should be "
"displayed."
msgstr ""
+"Dĺžka trvania v sekundách, počas ktorého by mali byť zobrazované stručné "
+"oznámenia."
#: desktop/org.gnome.Geary.gschema.xml:146
msgid "List of optional plugins"
@@ -958,13 +962,13 @@ msgstr "Dušan Kazik <prescott66@gmail.com>"
#. / Warning printed to the console when a deprecated
#. / command line option is used.
-#: src/client/application/application-client.vala:1047
+#: src/client/application/application-client.vala:1045
msgid "The `--hidden` option is deprecated and will be removed in the future."
msgstr "Voľba „--hidden“ je zastaraná a bude v budúcnosti odstránená."
#. / Command line warning, string substitution
#. / is the given argument
-#: src/client/application/application-client.vala:1080
+#: src/client/application/application-client.vala:1078
#, c-format
msgid "Unrecognised program argument: “%s”"
msgstr "Nerozpoznaný parameter programu: „%s“"
@@ -1140,21 +1144,21 @@ msgstr ""
#: src/client/application/application-controller.vala:1501
#, c-format
msgid "Email sent to %s"
-msgstr "Email pre %s bol odoslaný"
+msgstr "Email pre príjemcu %s bol odoslaný"
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
#: src/client/application/application-controller.vala:2491
#, c-format
msgid "Email to %s queued for delivery"
-msgstr "Email pre %s bol zaradený do fronty na doručenie"
+msgstr "Email pre príjemcu %s bol zaradený do fronty na doručenie"
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
#: src/client/application/application-controller.vala:2555
#, c-format
msgid "Email to %s saved"
-msgstr "Email pre %s bol uložený"
+msgstr "Email pre príjemcu %s bol uložený"
#. / Translators: A label for an in-app notification.
#: src/client/application/application-controller.vala:2570
@@ -1167,7 +1171,7 @@ msgstr "Nepodarilo sa obnoviť tvorcu správ"
#: src/client/application/application-controller.vala:2613
#, c-format
msgid "Email to %s discarded"
-msgstr "Email pre %s bol zahodený"
+msgstr "Email pre príjemcu %s bol zahodený"
#. Translators: An info bar status label
#: src/client/application/application-main-window.vala:540
@@ -1334,7 +1338,7 @@ msgstr "Uložiť ako"
#: src/client/components/components-inspector.vala:230
#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
-#: ui/accounts_editor_servers_pane.ui:17
+#: ui/accounts_editor_servers_pane.ui:17 ui/composer-headerbar.ui:61
msgid "Cancel"
msgstr "Zrušiť"
@@ -2185,7 +2189,6 @@ msgstr "Vytvára opakovane použiteľné šablóny na odosielanie emailov"
#. included.
#: src/client/plugin/email-templates/email-templates.vala:29
#: src/client/plugin/mail-merge/mail-merge.vala:29
-#| msgid "Sent | Sent Mail | Sent Email | Sent E-Mail"
msgid "Templates | Template Mail | Template Email | Template E-Mail"
msgstr "Šablóny | Poštová šablóna | Šablóna emailu | Šablóna e-mailu"
@@ -2195,11 +2198,12 @@ msgstr "Šablóny | Poštová šablóna | Šablóna emailu | Šablóna e-mailu"
msgid "Templates"
msgstr "Šablóny"
+# DK: šablóna
#. Translators: Info bar button label for creating a
#. new email template
#: src/client/plugin/email-templates/email-templates.vala:282
msgid "New"
-msgstr "Nový"
+msgstr "Nová"
#. Translators: Infobar status label for an email template
#: src/client/plugin/email-templates/email-templates.vala:293
@@ -2233,11 +2237,11 @@ msgstr "Upraviť"
#: src/client/plugin/mail-merge/mail-merge.vala:389
#: src/client/plugin/mail-merge/mail-merge.vala:488
msgid "Mail Merge"
-msgstr ""
+msgstr "Zlúčenie pošty"
#: src/client/plugin/mail-merge/mail-merge.plugin.desktop.in:6
msgid "Fill in and send email templates using a spreadsheet"
-msgstr ""
+msgstr "Vyplní šablóny emailov na odoslanie pomocou tabuľkového súboru"
#. Translators: Info bar label for starting sending a mail
#. merge
@@ -2258,21 +2262,21 @@ msgstr "Pozastaviť"
#, c-format
msgid "Sent %u of %u"
msgid_plural "Sent %u of %u"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "Odoslaných %u emailov z %u"
+msgstr[1] "Odoslaný %u email z %u"
+msgstr[2] "Odoslané %u emaily z %u"
#. Translators: Infobar status label for an email mail merge
#. template
#: src/client/plugin/mail-merge/mail-merge.vala:324
msgid "Mail merge template"
-msgstr ""
+msgstr "Šablóna pre zlúčenie pošty"
#. Translators: Info bar button label for performing a
#. mail-merge on an email template
#: src/client/plugin/mail-merge/mail-merge.vala:328
msgid "Merge"
-msgstr ""
+msgstr "Zlúčiť"
#. / Translators: Action bar menu button label for
#. / mail-merge plugin
@@ -2290,10 +2294,8 @@ msgid "Messaging Menu"
msgstr "Ponuka správ"
#: src/client/plugin/messaging-menu/messaging-menu.plugin.desktop.in:5
-#, fuzzy
-#| msgid "Show notifications for new mail"
msgid "Displays Unity Messaging Menu notifications for new email"
-msgstr "Zobraziť upozornenia pre novú poštu"
+msgstr "Zobrazí oznámenia pre novú poštu v ponuke správ prostredia Unity"
#: src/client/plugin/messaging-menu/messaging-menu.vala:79
#, c-format
@@ -2301,14 +2303,12 @@ msgid "%s — New Messages"
msgstr "%s — Nové správy"
#: src/client/plugin/sent-sound/sent-sound.plugin.desktop.in:4
-#, fuzzy
-#| msgid "not found"
msgid "Sent Sound"
-msgstr "nenájdené"
+msgstr "Zvuk po odoslaní"
#: src/client/plugin/sent-sound/sent-sound.plugin.desktop.in:5
msgid "Plays the desktop sent-mail sound when an email is sent"
-msgstr ""
+msgstr "Prehrá systémový zvuk po odoslaní emailu"
#. Translators: Info bar button label for emptying
#. trash/spam folders
@@ -3190,7 +3190,7 @@ msgstr "Prepne pridávanie nových položiek záznamu"
#. Tooltip for inspector button
#: ui/components-inspector.ui:55
msgid "Add a marker entry to the log"
-msgstr ""
+msgstr "Pridá značku do záznamu"
#. Tooltip for inspector button
#. Tooltip for problem report button
@@ -3237,10 +3237,8 @@ msgstr "Vždy načítať vzdialené obrázky"
#. Title label on contact popover
#: ui/conversation-contact-popover.ui:264
-#, fuzzy
-#| msgid "Remove email address"
msgid "Deceptive email address"
-msgstr "Odstráni emailové adresy"
+msgstr "Podvodná emailová adresa"
#. Contact popover label
#: ui/conversation-contact-popover.ui:294
@@ -3662,12 +3660,9 @@ msgid "Unquote text"
msgstr "Zrušenie citácie textu"
#: ui/gtk/help-overlay.ui:464
-#, fuzzy
-#| msgctxt "shortcut window"
-#| msgid "Rich text mode"
msgctxt "shortcut window"
msgid "Rich text editing"
-msgstr "Formátovanie textu"
+msgstr "Úprava formátovaného textu"
#: ui/gtk/help-overlay.ui:468
msgctxt "shortcut window"
--
2.29.2

View File

@ -0,0 +1,75 @@
From 571c10add8f8b0ad33031935f5de1bd6ff6fed2b Mon Sep 17 00:00:00 2001
From: Daniel Mustieles <daniel.mustieles@gmail.com>
Date: Wed, 23 Sep 2020 09:26:43 +0200
Subject: [PATCH 018/124] Updated Spanish translation
---
po/es.po | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/po/es.po b/po/es.po
index c387333b..fc6a4f53 100644
--- a/po/es.po
+++ b/po/es.po
@@ -18,8 +18,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary-0.4.1\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-08-29 01:59+0000\n"
-"PO-Revision-Date: 2020-08-26 10:09+0200\n"
+"POT-Creation-Date: 2020-09-21 11:52+0000\n"
+"PO-Revision-Date: 2020-09-23 09:16+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
"Language: es_ES\n"
@@ -27,7 +27,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Gtranslator 3.36.0\n"
+"X-Generator: Gtranslator 3.38.0\n"
#: desktop/geary-attach.contract.desktop.in:3
msgid "Send by email"
@@ -982,13 +982,13 @@ msgstr ""
#. / Warning printed to the console when a deprecated
#. / command line option is used.
-#: src/client/application/application-client.vala:1047
+#: src/client/application/application-client.vala:1045
msgid "The `--hidden` option is deprecated and will be removed in the future."
msgstr "La opción «--hidden» está obsoleta y se eliminará en el futuro."
#. / Command line warning, string substitution
#. / is the given argument
-#: src/client/application/application-client.vala:1080
+#: src/client/application/application-client.vala:1078
#, c-format
msgid "Unrecognised program argument: “%s”"
msgstr "Opción de la línea de comandos no reconocida: «%s»"
@@ -1345,7 +1345,7 @@ msgstr "Guardar como"
#: src/client/components/components-inspector.vala:230
#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
-#: ui/accounts_editor_servers_pane.ui:17
+#: ui/accounts_editor_servers_pane.ui:17 ui/composer-headerbar.ui:61
msgid "Cancel"
msgstr "Cancelar"
@@ -2248,11 +2248,11 @@ msgstr "Pausar"
#. folder. The first string substitution the number of email
#. already sent, the second is the total number to send.
#: src/client/plugin/mail-merge/mail-merge.vala:240
-#, fuzzy, c-format
+#, c-format
#| msgid "Sent %u of %u"
msgid "Sent %u of %u"
msgid_plural "Sent %u of %u"
-msgstr[0] "Enviados %u de %u"
+msgstr[0] "Enviado %u de %u"
msgstr[1] "Enviados %u de %u"
#. Translators: Infobar status label for an email mail merge
--
2.29.2

View File

@ -0,0 +1,31 @@
From 7f7c55e79a0ec40ec3fae5c86568e4c1b6cc37df Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 25 Sep 2020 08:25:34 +1000
Subject: [PATCH 020/124] Application.CertificateManager: Fix critical when no
GCR trust stores
Ensure GCR store not null before accessing it.
---
src/client/application/application-certificate-manager.vala | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/client/application/application-certificate-manager.vala b/src/client/application/application-certificate-manager.vala
index ff0e7785..d9e40fcd 100644
--- a/src/client/application/application-certificate-manager.vala
+++ b/src/client/application/application-certificate-manager.vala
@@ -74,8 +74,10 @@ public class Application.CertificateManager : GLib.Object {
bool has_rw_store = false;
if (has_uris) {
Gck.Slot? store = Gcr.pkcs11_get_trust_store_slot();
- has_rw_store = !store.has_flags(CKF_WRITE_PROTECTED);
- debug("GCR store is R/W: %s", has_rw_store.to_string());
+ if (store != null) {
+ has_rw_store = !store.has_flags(CKF_WRITE_PROTECTED);
+ debug("GCR store is R/W: %s", has_rw_store.to_string());
+ }
}
return has_rw_store;
--
2.29.2

View File

@ -0,0 +1,46 @@
From 0475d29f84ef13861fdc5ee2e747c00644c002fc Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 25 Sep 2020 08:27:36 +1000
Subject: [PATCH 021/124] Application.CertificateManager: Warn when GCR not
able to access stores
At least let people know somehow their GCR setup is lacking.
---
.../application-certificate-manager.vala | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/client/application/application-certificate-manager.vala b/src/client/application/application-certificate-manager.vala
index d9e40fcd..3add2206 100644
--- a/src/client/application/application-certificate-manager.vala
+++ b/src/client/application/application-certificate-manager.vala
@@ -68,7 +68,13 @@ public class Application.CertificateManager : GLib.Object {
!Geary.String.is_empty(Gcr.pkcs11_get_trust_store_uri()) &&
Gcr.pkcs11_get_trust_lookup_uris().length > 0
);
- debug("GCR slot URIs found: %s", has_uris.to_string());
+ if (has_uris) {
+ debug("GCR slot URIs found: %s", has_uris.to_string());
+ } else {
+ warning(
+ "No GCR slot URIs found, GCR certificate pinning unavailable"
+ );
+ }
}
bool has_rw_store = false;
@@ -77,6 +83,12 @@ public class Application.CertificateManager : GLib.Object {
if (store != null) {
has_rw_store = !store.has_flags(CKF_WRITE_PROTECTED);
debug("GCR store is R/W: %s", has_rw_store.to_string());
+ } else {
+ warning("No GCR store found, GCR certificate pinning unavailable");
+ }
+
+ if (!has_rw_store) {
+ warning("GCR store is not RW, GCR certificate pinning unavailable");
}
}
--
2.29.2

View File

@ -0,0 +1,35 @@
From 2093aa32292e11455648d62d8b7a992532aac04e Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 23 Sep 2020 08:44:04 +1000
Subject: [PATCH 022/124] Geary.Imap.Session: Avoid critical when client
session logged out
If the client session is being logging out but some other tasks is
still attempting to use it, getting a mailbox will assume at least
one personal namespace exists, but it will have been cleared.
Add a check and throw an exception if none are present, so at least
it is handled in a well defined way.
Fixes #986
---
src/engine/imap/transport/imap-client-session.vala | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala
index f42112f2..81d892ef 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -599,6 +599,9 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
}
if (ns == null) {
// fall back to the default personal namespace
+ if (this.personal_namespaces.is_empty) {
+ throw new ImapError.UNAVAILABLE("No personal namespace");
+ }
ns = this.personal_namespaces[0];
}
--
2.29.2

View File

@ -0,0 +1,47 @@
From 1c951f890840b91885d5f3752bb8bc3fdfc26324 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 23 Sep 2020 08:48:45 +1000
Subject: [PATCH 023/124] Geary.Imap.SessionObject: Ensure the session is
connected when accessed
Ensure the client session is connected as well as non-null when being
accessed, so that if being logged out it is also treated as being
disconnected.
---
src/engine/imap/api/imap-session-object.vala | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala
index 4937d462..ee1ac09f 100644
--- a/src/engine/imap/api/imap-session-object.vala
+++ b/src/engine/imap/api/imap-session-object.vala
@@ -83,16 +83,20 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
}
/**
- * Obtains IMAP session the server for use by this object.
+ * Returns a valid IMAP client session for use by this object.
*
- * @throws ImapError.NOT_CONNECTED if the session with the server
- * server has been dropped via {@link close}, or because
- * the connection was lost.
+ * @throws ImapError.NOT_CONNECTED if the client session has been
+ * dropped via {@link close}, if the client session is logging out
+ * or has been closed, or because the connection to the server was
+ * lost.
*/
protected ClientSession claim_session()
throws ImapError {
- if (this.session == null) {
- throw new ImapError.NOT_CONNECTED("IMAP object has no session");
+ if (this.session == null ||
+ this.session.get_protocol_state() == NOT_CONNECTED) {
+ throw new ImapError.NOT_CONNECTED(
+ "IMAP object has no session or is not connected"
+ );
}
return this.session;
}
--
2.29.2

View File

@ -0,0 +1,101 @@
From b209f84e5895d7ae3b9ca200f36343d162914932 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 23 Sep 2020 09:11:50 +1000
Subject: [PATCH 024/124] Geary.Imap.FolderSession: Ensure client session is
selected when accessed
Ensure the underlying ClientSession object is in the SELECTED state
for the correct mailbox when obtaining it.
---
src/engine/imap/api/imap-folder-session.vala | 31 ++++++++++++++++----
src/engine/imap/api/imap-session-object.vala | 2 +-
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 092c06cd..98db3088 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -38,6 +38,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
/** Determines if this folder accepts custom IMAP flags. */
public Trillian accepts_user_flags { get; private set; default = Trillian.UNKNOWN; }
+ private MailboxSpecifier mailbox;
+
private Quirks quirks;
private Nonblocking.Mutex cmd_mutex = new Nonblocking.Mutex();
@@ -107,9 +109,9 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
session.search.connect(on_search);
session.status_response_received.connect(on_status_response);
- MailboxSpecifier mailbox = session.get_mailbox_for_path(folder.path);
+ this.mailbox = session.get_mailbox_for_path(folder.path);
StatusResponse? response = yield session.select_async(
- mailbox, cancellable
+ this.mailbox, cancellable
);
throw_on_not_ok(response, "SELECT " + this.folder.path.to_string());
@@ -1107,8 +1109,6 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
Geary.EmailFlags? flags,
GLib.DateTime? date_received)
throws GLib.Error {
- ClientSession session = claim_session();
-
MessageFlags? msg_flags = null;
if (flags != null) {
Imap.EmailFlags imap_flags = Imap.EmailFlags.from_api_email_flags(flags);
@@ -1121,9 +1121,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
if (date_received != null)
internaldate = new InternalDate.from_date_time(date_received);
- MailboxSpecifier mailbox = session.get_mailbox_for_path(this.folder.path);
AppendCommand cmd = new AppendCommand(
- mailbox,
+ this.mailbox,
msg_flags,
internaldate,
message.get_rfc822_buffer(),
@@ -1161,6 +1160,26 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
);
}
+ /**
+ * Returns a valid IMAP client session for use by this object.
+ *
+ * In addition to the checks made by {@link
+ * SessionObject.claim_session}, this method also ensures that the
+ * IMAP session is in the SELECTED state for the correct mailbox.
+ */
+ protected override ClientSession claim_session()
+ throws ImapError {
+ var session = base.claim_session();
+ if (session.get_protocol_state() != SELECTED &&
+ !this.mailbox.equal_to(session.selected_mailbox)) {
+ throw new ImapError.NOT_CONNECTED(
+ "IMAP object no longer SELECTED for %s",
+ this.mailbox.to_string()
+ );
+ }
+ return session;
+ }
+
// HACK: See https://bugzilla.gnome.org/show_bug.cgi?id=714902
//
// Detect when a server has returned a BAD response to FETCH
diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala
index ee1ac09f..d47c6950 100644
--- a/src/engine/imap/api/imap-session-object.vala
+++ b/src/engine/imap/api/imap-session-object.vala
@@ -90,7 +90,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
* or has been closed, or because the connection to the server was
* lost.
*/
- protected ClientSession claim_session()
+ protected virtual ClientSession claim_session()
throws ImapError {
if (this.session == null ||
this.session.get_protocol_state() == NOT_CONNECTED) {
--
2.29.2

View File

@ -0,0 +1,134 @@
From ac425f57b134c2264723e20ea15319ab615f0f9d Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 23 Sep 2020 09:17:28 +1000
Subject: [PATCH 025/124] Geary.Imap.SessionObject: Rename `claim_session` to
`get_session`
Don't over-sell what it does.
---
src/engine/imap/api/imap-account-session.vala | 10 +++++-----
src/engine/imap/api/imap-folder-session.vala | 14 +++++++-------
src/engine/imap/api/imap-session-object.vala | 2 +-
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/engine/imap/api/imap-account-session.vala b/src/engine/imap/api/imap-account-session.vala
index 238bd85c..813d9e5e 100644
--- a/src/engine/imap/api/imap-account-session.vala
+++ b/src/engine/imap/api/imap-account-session.vala
@@ -45,7 +45,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
*/
public async FolderPath get_default_personal_namespace(Cancellable? cancellable)
throws Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
Gee.List<Namespace> personal = session.get_personal_namespaces();
if (personal.is_empty) {
throw new ImapError.INVALID("No personal namespace found");
@@ -69,7 +69,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
public bool is_folder_path_valid(FolderPath? path) throws GLib.Error {
bool is_valid = false;
if (path != null) {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
try {
session.get_mailbox_for_path(path);
is_valid = true;
@@ -94,7 +94,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
Geary.Folder.SpecialUse? use,
Cancellable? cancellable)
throws Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
MailboxSpecifier mailbox = session.get_mailbox_for_path(path);
bool can_create_special = session.capabilities.has_capability(Capabilities.CREATE_SPECIAL_USE);
CreateCommand cmd = (
@@ -125,7 +125,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
public async Imap.Folder fetch_folder_async(FolderPath path,
Cancellable? cancellable)
throws Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
Imap.Folder? folder = this.folders.get(path);
if (folder == null) {
Gee.List<MailboxInformation>? mailboxes = yield send_list_async(
@@ -169,7 +169,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
fetch_child_folders_async(FolderPath parent,
GLib.Cancellable? cancellable)
throws GLib.Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
Gee.List<Imap.Folder> children = new Gee.ArrayList<Imap.Folder>();
Gee.List<MailboxInformation> mailboxes = yield send_list_async(
session, parent, true, cancellable
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 98db3088..8b4212e6 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -127,7 +127,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
*/
public async void enable_idle(Cancellable? cancellable)
throws Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
int token = yield this.cmd_mutex.claim_async(cancellable);
Error? cmd_err = null;
try {
@@ -303,7 +303,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
Gee.Set<Imap.UID>? search_results,
GLib.Cancellable? cancellable)
throws GLib.Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
Gee.Map<Command, StatusResponse>? responses = null;
int token = yield this.cmd_mutex.claim_async(cancellable);
@@ -648,7 +648,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
public async void remove_email_async(Gee.List<MessageSet> msg_sets,
GLib.Cancellable? cancellable)
throws GLib.Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
Gee.List<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
flags.add(MessageFlag.DELETED);
@@ -721,7 +721,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
FolderPath destination,
GLib.Cancellable? cancellable)
throws GLib.Error {
- ClientSession session = claim_session();
+ ClientSession session = get_session();
MailboxSpecifier mailbox = session.get_mailbox_for_path(destination);
CopyCommand cmd = new CopyCommand(msg_set, mailbox, cancellable);
@@ -1164,12 +1164,12 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
* Returns a valid IMAP client session for use by this object.
*
* In addition to the checks made by {@link
- * SessionObject.claim_session}, this method also ensures that the
+ * SessionObject.get_session}, this method also ensures that the
* IMAP session is in the SELECTED state for the correct mailbox.
*/
- protected override ClientSession claim_session()
+ protected override ClientSession get_session()
throws ImapError {
- var session = base.claim_session();
+ var session = base.get_session();
if (session.get_protocol_state() != SELECTED &&
!this.mailbox.equal_to(session.selected_mailbox)) {
throw new ImapError.NOT_CONNECTED(
diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala
index d47c6950..4a46ae1e 100644
--- a/src/engine/imap/api/imap-session-object.vala
+++ b/src/engine/imap/api/imap-session-object.vala
@@ -90,7 +90,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
* or has been closed, or because the connection to the server was
* lost.
*/
- protected virtual ClientSession claim_session()
+ protected virtual ClientSession get_session()
throws ImapError {
if (this.session == null ||
this.session.get_protocol_state() == NOT_CONNECTED) {
--
2.29.2

View File

@ -0,0 +1,154 @@
From ed4ba33127795a7ffefb6517ed57185ef44fe1c7 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 15:58:40 +1000
Subject: [PATCH 026/124] Geary.State.Machine: Support GObject notify signal
for state changes
Modernise the API a bit by using properties instead of explicit
getters/setters, an hence support GObject notify signals when the
state property changes.
---
.../imap/transport/imap-client-session.vala | 9 ++---
.../imap/transport/imap-deserializer.vala | 4 +-
src/engine/state/state-machine.vala | 37 ++++++-------------
3 files changed, 18 insertions(+), 32 deletions(-)
diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala
index 81d892ef..91137f1e 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -483,11 +483,10 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
};
fsm = new Geary.State.Machine(machine_desc, mappings, on_ignored_transition);
- fsm.set_logging(false);
}
~ClientSession() {
- switch (fsm.get_state()) {
+ switch (fsm.state) {
case State.NOT_CONNECTED:
case State.CLOSED:
// no problem-o
@@ -782,7 +781,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
private bool on_greeting_timeout() {
// if still in CONNECTING state, the greeting never arrived
- if (fsm.get_state() == State.CONNECTING)
+ if (fsm.state == State.CONNECTING)
fsm.issue(Event.TIMEOUT);
return false;
@@ -1645,12 +1644,12 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
return (this.selected_mailbox == null)
? new Logging.State(
this,
- this.fsm.get_state_string(fsm.get_state())
+ this.fsm.get_state_string(fsm.state)
)
: new Logging.State(
this,
"%s:%s selected %s",
- this.fsm.get_state_string(fsm.get_state()),
+ this.fsm.get_state_string(fsm.state),
this.selected_mailbox.to_string(),
this.selected_readonly ? "RO" : "RW"
);
diff --git a/src/engine/imap/transport/imap-deserializer.vala b/src/engine/imap/transport/imap-deserializer.vala
index 249f7c85..559a5e78 100644
--- a/src/engine/imap/transport/imap-deserializer.vala
+++ b/src/engine/imap/transport/imap-deserializer.vala
@@ -294,7 +294,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source {
/** {@inheritDoc} */
public Logging.State to_logging_state() {
- return new Logging.State(this, fsm.get_state_string(fsm.get_state()));
+ return new Logging.State(this, fsm.get_state_string(fsm.state));
}
/** Sets the connection's logging parent. */
@@ -429,7 +429,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source {
}
private Mode get_mode() {
- switch (fsm.get_state()) {
+ switch (fsm.state) {
case State.LITERAL_DATA:
return Mode.BLOCK;
diff --git a/src/engine/state/state-machine.vala b/src/engine/state/state-machine.vala
index 351babb8..ef32555d 100644
--- a/src/engine/state/state-machine.vala
+++ b/src/engine/state/state-machine.vala
@@ -5,13 +5,20 @@
*/
public class Geary.State.Machine : BaseObject {
+
+ /** The state machine's current state. */
+ public uint state { get; private set; }
+
+ /** Determines if the state machine crashes your app when mis-configured. */
+ public bool abort_on_no_transition { get; set; default = true; }
+
+ /** Determines if transition logging is enabled. */
+ public bool logging { get; private set; default = false; }
+
private Geary.State.MachineDescriptor descriptor;
- private uint state;
private Mapping[,] transitions;
private unowned Transition? default_transition;
private bool locked = false;
- private bool abort_on_no_transition = true;
- private bool logging = false;
private unowned PostTransition? post_transition = null;
private void *post_user = null;
private Object? post_object = null;
@@ -39,26 +46,6 @@ public class Geary.State.Machine : BaseObject {
}
}
- public uint get_state() {
- return state;
- }
-
- public bool get_abort_on_no_transition() {
- return abort_on_no_transition;
- }
-
- public void set_abort_on_no_transition(bool abort) {
- abort_on_no_transition = abort;
- }
-
- public void set_logging(bool logging) {
- this.logging = logging;
- }
-
- public bool is_logging() {
- return logging;
- }
-
public uint issue(uint event, void *user = null, Object? object = null, Error? err = null) {
assert(event < descriptor.event_count);
assert(state < descriptor.state_count);
@@ -70,7 +57,7 @@ public class Geary.State.Machine : BaseObject {
string msg = "%s: No transition defined for %s@%s".printf(to_string(),
descriptor.get_event_string(event), descriptor.get_state_string(state));
- if (get_abort_on_no_transition())
+ if (this.abort_on_no_transition)
error(msg);
else
critical(msg);
@@ -96,7 +83,7 @@ public class Geary.State.Machine : BaseObject {
}
locked = false;
- if (is_logging())
+ if (this.logging)
message("%s: %s", to_string(), get_transition_string(old_state, event, state));
// Perform post-transition if registered
--
2.29.2

View File

@ -0,0 +1,561 @@
From 41be8693d4481d6a50f49e3bf6698e9103d0bfa7 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 16:00:35 +1000
Subject: [PATCH 027/124] Geary.Imap.ClientSession: Treat logout as disconnect
Convert `get_protocol_state` to an automatic property, so that rather
than requiring explcit signals for lifecycle events, a GObject notify
signal can be used instead.
Convert disconnect signal to a property so it can be accessed if needed.
Convert code listening to the disconnect signal to listen to notify
signals for `protocol_state` instead, and hence also treat the session
as disconnected when a logout is in progress.
Fixes #986
---
src/engine/imap/api/imap-client-service.vala | 38 +++---
src/engine/imap/api/imap-folder-session.vala | 2 +-
src/engine/imap/api/imap-session-object.vala | 26 ++--
.../imap/transport/imap-client-session.vala | 118 ++++++++++--------
.../transport/imap-client-session-test.vala | 40 +++---
test/integration/imap/client-session.vala | 2 +-
6 files changed, 130 insertions(+), 96 deletions(-)
diff --git a/src/engine/imap/api/imap-client-service.vala b/src/engine/imap/api/imap-client-service.vala
index e0aad41b..da3598f3 100644
--- a/src/engine/imap/api/imap-client-service.vala
+++ b/src/engine/imap/api/imap-client-service.vala
@@ -252,7 +252,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
if (!disconnect) {
// If the session has a mailbox selected, close it before
// adding it back to the pool
- ClientSession.ProtocolState proto = session.get_protocol_state();
+ ClientSession.ProtocolState proto = session.protocol_state;
if (proto == ClientSession.ProtocolState.SELECTED ||
proto == ClientSession.ProtocolState.SELECTING) {
// always close mailbox to return to authorized state
@@ -263,7 +263,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
session.to_string(), imap_error.message);
disconnect = true;
}
- if (session.get_protocol_state() != AUTHORIZED) {
+ if (session.protocol_state != AUTHORIZED) {
// Closing it didn't leave it in the desired
// state, so drop it
disconnect = true;
@@ -393,7 +393,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
/** Determines if a session is valid, disposing of it if not. */
private async bool check_session(ClientSession target, bool claiming) {
bool valid = false;
- switch (target.get_protocol_state()) {
+ switch (target.protocol_state) {
case ClientSession.ProtocolState.AUTHORIZED:
case ClientSession.ProtocolState.CLOSING_MAILBOX:
valid = true;
@@ -472,7 +472,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
// Only bother tracking disconnects and enabling keeping alive
// now the session is properly established.
- new_session.disconnected.connect(on_disconnected);
+ new_session.notify["disconnected"].connect(on_session_disconnected);
new_session.enable_keepalives(selected_keepalive_sec,
unselected_keepalive_sec,
selected_with_idle_keepalive_sec);
@@ -509,7 +509,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
}
private async void disconnect_session(ClientSession session) {
- if (session.get_protocol_state() != NOT_CONNECTED) {
+ if (session.protocol_state != NOT_CONNECTED) {
debug("Logging out session: %s", session.to_string());
// No need to remove it after logging out, the
// disconnected handler will do that for us.
@@ -548,21 +548,27 @@ public class Geary.Imap.ClientService : Geary.ClientService {
}
if (removed) {
- session.disconnected.disconnect(on_disconnected);
+ session.notify["disconnected"].connect(on_session_disconnected);
}
return removed;
}
- private void on_disconnected(ClientSession session,
- ClientSession.DisconnectReason reason) {
- debug(
- "Session disconnected: %s: %s",
- session.to_string(), reason.to_string()
- );
- this.remove_session_async.begin(
- session,
- (obj, res) => { this.remove_session_async.end(res); }
- );
+ private void on_session_disconnected(GLib.Object source,
+ GLib.ParamSpec param) {
+ var session = source as ClientSession;
+ if (session != null &&
+ session.protocol_state == NOT_CONNECTED &&
+ session.disconnected != null) {
+ debug(
+ "Session disconnected: %s: %s",
+ session.to_string(),
+ session.disconnected.to_string()
+ );
+ this.remove_session_async.begin(
+ session,
+ (obj, res) => { this.remove_session_async.end(res); }
+ );
+ }
}
}
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 8b4212e6..8a2290cb 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -1170,7 +1170,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
protected override ClientSession get_session()
throws ImapError {
var session = base.get_session();
- if (session.get_protocol_state() != SELECTED &&
+ if (session.protocol_state != SELECTED &&
!this.mailbox.equal_to(session.selected_mailbox)) {
throw new ImapError.NOT_CONNECTED(
"IMAP object no longer SELECTED for %s",
diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala
index 4a46ae1e..80695fca 100644
--- a/src/engine/imap/api/imap-session-object.vala
+++ b/src/engine/imap/api/imap-session-object.vala
@@ -39,7 +39,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
*/
protected SessionObject(ClientSession session) {
this.session = session;
- this.session.disconnected.connect(on_disconnected);
+ this.session.notify["protocol-state"].connect(on_session_state_change);
}
~SessionObject() {
@@ -63,7 +63,9 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
this.session = null;
if (old_session != null) {
- old_session.disconnected.disconnect(on_disconnected);
+ old_session.notify["protocol-state"].disconnect(
+ on_session_state_change
+ );
}
return old_session;
@@ -93,7 +95,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
protected virtual ClientSession get_session()
throws ImapError {
if (this.session == null ||
- this.session.get_protocol_state() == NOT_CONNECTED) {
+ this.session.protocol_state == NOT_CONNECTED) {
throw new ImapError.NOT_CONNECTED(
"IMAP object has no session or is not connected"
);
@@ -101,11 +103,19 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
return this.session;
}
- private void on_disconnected(ClientSession.DisconnectReason reason) {
- debug("Disconnected %s", reason.to_string());
-
- close();
- disconnected(reason);
+ private void on_session_state_change() {
+ if (this.session != null &&
+ this.session.protocol_state == NOT_CONNECTED) {
+ // Disconnect reason will null when the session is being
+ // logged out but the logout command has not yet been
+ // completed.
+ var reason = (
+ this.session.disconnected ??
+ ClientSession.DisconnectReason.LOCAL_CLOSE
+ );
+ close();
+ disconnected(reason);
+ }
}
}
diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala
index 91137f1e..ba125616 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -90,7 +90,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
*
* See [[http://tools.ietf.org/html/rfc3501#section-3]]
*
- * @see get_protocol_state
+ * @see protocol_state
*/
public enum ProtocolState {
NOT_CONNECTED,
@@ -230,6 +230,55 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
"Geary.Imap.ClientSession", State.NOT_CONNECTED, State.COUNT, Event.COUNT,
state_to_string, event_to_string);
+
+ /**
+ * Returns the current IMAP protocol state for the session.
+ */
+ public ProtocolState protocol_state {
+ get {
+ var state = ProtocolState.NOT_CONNECTED;
+ switch (fsm.state) {
+ case State.NOT_CONNECTED:
+ case State.LOGOUT:
+ case State.CLOSED:
+ state = NOT_CONNECTED;
+ break;
+
+ case State.NOAUTH:
+ state = UNAUTHORIZED;
+ break;
+
+ case State.AUTHORIZED:
+ state = AUTHORIZED;
+ break;
+
+ case State.SELECTED:
+ state = SELECTED;
+ break;
+
+ case State.CONNECTING:
+ state = CONNECTING;
+ break;
+
+ case State.AUTHORIZING:
+ state = AUTHORIZING;
+ break;
+
+ case State.SELECTING:
+ state = SELECTING;
+ break;
+
+ case State.CLOSING_MAILBOX:
+ state = CLOSING_MAILBOX;
+ break;
+ }
+ return state;
+ }
+ }
+
+ /** Specifies the reason the session was disconnected, if any. */
+ public DisconnectReason? disconnected { get; private set; default = null; }
+
/**
* Set of IMAP extensions reported as being supported by the server.
*
@@ -330,9 +379,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
// Connection state changes
//
- /** Emitted when the session is disconnected for any reason. */
- public signal void disconnected(DisconnectReason reason);
-
/** Emitted when an IMAP command status response is received. */
public signal void status_response_received(StatusResponse status_response);
@@ -482,7 +528,14 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
new Geary.State.Mapping(State.CLOSED, Event.RECV_ERROR, Geary.State.nop),
};
- fsm = new Geary.State.Machine(machine_desc, mappings, on_ignored_transition);
+ this.fsm = new Geary.State.Machine(
+ machine_desc,
+ mappings,
+ on_ignored_transition
+ );
+ this.fsm.notify["state"].connect(
+ () => this.notify_property("protocol_state")
+ );
}
~ClientSession() {
@@ -493,7 +546,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
break;
default:
- warning("ClientSession ref dropped while still active");
+ GLib.warning("ClientSession ref dropped while still active");
}
}
@@ -636,43 +689,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
return delim;
}
- /**
- * Returns the current {@link ProtocolState} of the {@link ClientSession} and, if selected,
- * the current mailbox.
- */
- public ProtocolState get_protocol_state() {
- switch (fsm.get_state()) {
- case State.NOT_CONNECTED:
- case State.LOGOUT:
- case State.CLOSED:
- return ProtocolState.NOT_CONNECTED;
-
- case State.NOAUTH:
- return ProtocolState.UNAUTHORIZED;
-
- case State.AUTHORIZED:
- return ProtocolState.AUTHORIZED;
-
- case State.SELECTED:
- return ProtocolState.SELECTED;
-
- case State.CONNECTING:
- return ProtocolState.CONNECTING;
-
- case State.AUTHORIZING:
- return ProtocolState.AUTHORIZING;
-
- case State.SELECTING:
- return ProtocolState.SELECTING;
-
- case State.CLOSING_MAILBOX:
- return ProtocolState.CLOSING_MAILBOX;
-
- default:
- assert_not_reached();
- }
- }
-
// Some commands require waiting for a completion response in order to shift the state machine's
// State; this allocates such a wait, returning false if another command is outstanding also
// waiting for one to finish
@@ -1197,7 +1213,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
public void enable_idle()
throws GLib.Error {
if (this.is_idle_supported) {
- switch (get_protocol_state()) {
+ switch (this.protocol_state) {
case ProtocolState.AUTHORIZING:
case ProtocolState.AUTHORIZED:
case ProtocolState.SELECTED:
@@ -1218,7 +1234,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
unschedule_keepalive();
uint seconds;
- switch (get_protocol_state()) {
+ switch (this.protocol_state) {
case ProtocolState.NOT_CONNECTED:
case ProtocolState.CONNECTING:
return;
@@ -1555,10 +1571,11 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
MachineParams params = (MachineParams) object;
assert(params.cmd is LogoutCommand);
- if (!reserve_state_change_cmd(params, state, event))
- return state;
+ if (reserve_state_change_cmd(params, state, event)) {
+ state = State.LOGOUT;
+ }
- return State.LOGOUT;
+ return state;
}
private uint on_logging_out_recv_status(uint state,
@@ -1625,7 +1642,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
}
drop_connection();
- disconnected(DisconnectReason.LOCAL_CLOSE);
+ this.disconnected = DisconnectReason.LOCAL_CLOSE;
if (disconnect_err != null)
throw disconnect_err;
@@ -1661,6 +1678,8 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
}
private async void do_disconnect(DisconnectReason reason) {
+ this.disconnected = reason;
+
try {
yield this.cx.disconnect_async();
} catch (GLib.Error err) {
@@ -1668,7 +1687,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
}
drop_connection();
- disconnected(reason);
}
//
diff --git a/test/engine/imap/transport/imap-client-session-test.vala b/test/engine/imap/transport/imap-client-session-test.vala
index 2aed9211..c9a4165c 100644
--- a/test/engine/imap/transport/imap-client-session-test.vala
+++ b/test/engine/imap/transport/imap-client-session-test.vala
@@ -44,17 +44,17 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.disconnect_async.begin(null, this.async_completion);
test_article.disconnect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
TestServer.Result result = this.server.wait_for_script(this.main_loop);
assert_true(
@@ -148,13 +148,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.login_async.begin(
new Credentials(PASSWORD, "test", "password"),
@@ -162,11 +162,11 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.async_completion
);
test_article.login_async.end(async_result());
- assert_true(test_article.get_protocol_state() == AUTHORIZED);
+ assert_true(test_article.protocol_state == AUTHORIZED);
test_article.disconnect_async.begin(null, this.async_completion);
test_article.disconnect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
TestServer.Result result = this.server.wait_for_script(this.main_loop);
assert_true(
@@ -185,17 +185,17 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.logout_async.begin(null, this.async_completion);
test_article.logout_async.end(async_result());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
TestServer.Result result = this.server.wait_for_script(this.main_loop);
assert_true(
@@ -216,13 +216,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.login_async.begin(
new Credentials(PASSWORD, "test", "password"),
@@ -230,11 +230,11 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.async_completion
);
test_article.login_async.end(async_result());
- assert_true(test_article.get_protocol_state() == AUTHORIZED);
+ assert_true(test_article.protocol_state == AUTHORIZED);
test_article.logout_async.begin(null, this.async_completion);
test_article.logout_async.end(async_result());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
TestServer.Result result = this.server.wait_for_script(this.main_loop);
assert_true(
@@ -261,13 +261,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.initiate_session_async.begin(
new Credentials(PASSWORD, "test", "password"),
@@ -305,13 +305,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.initiate_session_async.begin(
new Credentials(PASSWORD, "test", "password"),
@@ -368,13 +368,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
var test_article = new ClientSession(new_endpoint(), new Quirks());
- assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+ assert_true(test_article.protocol_state == NOT_CONNECTED);
test_article.connect_async.begin(
CONNECT_TIMEOUT, null, this.async_completion
);
test_article.connect_async.end(async_result());
- assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+ assert_true(test_article.protocol_state == UNAUTHORIZED);
test_article.initiate_session_async.begin(
new Credentials(PASSWORD, "test", "password"),
diff --git a/test/integration/imap/client-session.vala b/test/integration/imap/client-session.vala
index ca813b79..257d7319 100644
--- a/test/integration/imap/client-session.vala
+++ b/test/integration/imap/client-session.vala
@@ -34,7 +34,7 @@ class Integration.Imap.ClientSession : TestCase {
}
public override void tear_down() throws GLib.Error {
- if (this.session.get_protocol_state() != NOT_CONNECTED) {
+ if (this.session.protocol_state != NOT_CONNECTED) {
this.session.disconnect_async.begin(null, this.async_completion);
this.session.disconnect_async.end(async_result());
}
--
2.29.2

View File

@ -0,0 +1,79 @@
From bd85c4f1a82c14fad0f909e5b1058c73d5b56c92 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 19:57:52 +1000
Subject: [PATCH 028/124] Composer.Widget: Fix criticals when "mailto:" has
empty body
---
src/client/composer/composer-widget.vala | 13 +++++++++----
test/client/composer/composer-widget-test.vala | 10 ++++++++++
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index ecc3fbfd..dab8cfd8 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -575,8 +575,11 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
Gee.HashMultiMap<string, string> headers = new Gee.HashMultiMap<string, string>();
if (mailto.has_prefix(MAILTO_URI_PREFIX)) {
// Parse the mailto link.
+ string? email = null;
string[] parts = mailto.substring(MAILTO_URI_PREFIX.length).split("?", 2);
- string email = Uri.unescape_string(parts[0]);
+ if (parts.length > 0) {
+ email = Uri.unescape_string(parts[0]);
+ }
string[] params = parts.length == 2 ? parts[1].split("&") : new string[0];
foreach (string param in params) {
string[] param_parts = param.split("=", 2);
@@ -587,14 +590,16 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
// Assemble the headers.
- if (email.length > 0 && headers.contains("to"))
+ if (!Geary.String.is_empty_or_whitespace(email) &&
+ headers.contains("to")) {
this.to = "%s,%s".printf(
email, Geary.Collection.first(headers.get("to"))
);
- else if (email.length > 0)
+ } else if (!Geary.String.is_empty_or_whitespace(email)) {
this.to = email;
- else if (headers.contains("to"))
+ } else if (headers.contains("to")) {
this.to = Geary.Collection.first(headers.get("to"));
+ }
if (headers.contains("cc"))
this.cc = Geary.Collection.first(headers.get("cc"));
diff --git a/test/client/composer/composer-widget-test.vala b/test/client/composer/composer-widget-test.vala
index c35b52e4..6b31c943 100644
--- a/test/client/composer/composer-widget-test.vala
+++ b/test/client/composer/composer-widget-test.vala
@@ -92,6 +92,7 @@ public class Composer.WidgetTest : TestCase {
add_test("load_empty_body", load_empty_body);
add_test("load_empty_body_to", load_empty_body_to);
add_test("load_mailto", load_mailto);
+ add_test("load_mailto_empty", load_mailto_empty);
add_test("load_context_edit", load_context_edit);
add_test("load_context_reply_sender", load_context_reply_sender);
add_test("load_context_reply_sender_with_reply_to", load_context_reply_sender_with_reply_to);
@@ -164,6 +165,15 @@ public class Composer.WidgetTest : TestCase {
assert_equal(widget.to, "mailto@example.com");
}
+ public void load_mailto_empty() throws GLib.Error {
+ var widget = new Widget(this.application, this.config, this.account);
+
+ widget.load_mailto.begin("mailto:", this.async_completion);
+ widget.load_mailto.end(async_result());
+
+ assert_equal(widget.to, "");
+ }
+
public void load_context_edit() throws GLib.Error {
var widget = new Widget(this.application, this.config, this.account);
var email = load_email(MESSAGE_WITH_REPLY_TO);
--
2.29.2

View File

@ -0,0 +1,58 @@
From 70186163e8bcf1a0644669fe7c9b2b56dceef9b5 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 22:46:40 +1000
Subject: [PATCH 029/124] Composer.Widget: Fix critical when immediately
detaching a new composer
New composers have no associated GLib Application instance, so when the
main window is already showing a composer and another is opened, the
new composer has no application to pass its window.
Fix by requiring `Composer.detach` be passed an application instance
and find an appropriate instance at each call site.
---
src/client/application/application-main-window.vala | 2 +-
src/client/composer/composer-widget.vala | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index e8428459..73b50e33 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -875,7 +875,7 @@ public class Application.MainWindow :
*/
internal void show_composer(Composer.Widget composer) {
if (this.has_composer) {
- composer.detach();
+ composer.detach(this.application);
} else {
// See if the currently displayed conversation contains
// any of the composer's referred emails (preferring the
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index dab8cfd8..37e93fb4 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -785,10 +785,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
/** Detaches the composer and opens it in a new window. */
- public void detach() {
+ public void detach(Application.Client application) {
Gtk.Widget? focused_widget = null;
- var application = this.container.top_window.application as Application.Client;
-
if (this.container != null) {
focused_widget = this.container.top_window.get_focus();
this.container.close();
@@ -2374,7 +2372,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
private void on_detach() {
- detach();
+ detach(this.container.top_window.application as Application.Client);
}
private void on_add_attachment() {
--
2.29.2

View File

@ -0,0 +1,126 @@
From 6fb365ebd45e283f770cde341c6332af66051ab6 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 23:37:50 +1000
Subject: [PATCH 030/124] Geary.RFC822.Message: Fix plain text file attachment
line ending conversion
Since RFC822 requires CRLF for line endings, it's not possible for a MUA
to determine the correct line ending for text attachments that use the
default or quoted-printable transfer encoding.
As such, always use a binary encoding for all non-body message parts
(which for now means always use Base64) so that line endings are
explicitly encoded and hence always decoded correctly.
Fixes #1001
---
src/engine/rfc822/rfc822-message.vala | 65 +++++++++++++--------------
1 file changed, 30 insertions(+), 35 deletions(-)
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 51563caf..e6cc17a4 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -494,7 +494,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
FileQueryInfoFlags.NONE
);
- GMime.Part part = new GMime.Part.with_type("text", "plain");
+ GMime.Part part = new GMime.Part();
part.set_disposition(disposition.serialize());
part.set_filename(file.get_basename());
@@ -504,10 +504,26 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
);
part.set_content_type(content_type);
+ // Always use a binary encoding since even when attaching
+ // text/plain parts, the line ending must always be preserved
+ // and this is not possible without a binary encoding. See
+ // https://gitlab.gnome.org/GNOME/geary/-/issues/1001
+ //
+ // TODO: The actual content encoding should be set based on
+ // the IMAP/SMTP server's supported encoding. For example, if
+ // 8-bit or binary is supported, then those should be used
+ // instead of Base64.
+ part.set_content_encoding(BASE64);
+
GMime.StreamGIO stream = new GMime.StreamGIO(file);
stream.set_owner(false);
+ part.set_content(
+ new GMime.DataWrapper.with_stream(
+ stream, GMime.ContentEncoding.BINARY
+ )
+ );
- return yield finalise_attachment_part(stream, part, content_type, cancellable);
+ return part;
}
/**
@@ -540,50 +556,29 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
);
}
- GMime.Part part = new GMime.Part.with_type("text", "plain");
+ GMime.Part part = new GMime.Part();
part.set_disposition(disposition.serialize());
part.set_filename(basename);
part.set_content_type(content_type);
- GMime.StreamMem stream = Utils.create_stream_mem(buffer);
-
- return yield finalise_attachment_part(stream, part, content_type, cancellable);
- }
-
- /**
- * Set encoding and content object on GMime part
- */
- private async GMime.Part finalise_attachment_part(GMime.Stream stream,
- GMime.Part part,
- GMime.ContentType content_type,
- GLib.Cancellable? cancellable)
- throws GLib.Error {
-
- // Text parts should be scanned fully to determine best
- // (i.e. most compact) transport encoding to use, but
- // that's usually fine since they tend to be
- // small. Non-text parts are nearly always going to be
- // binary, so we just assume they require Base64.
+ // Always use a binary encoding since even when attaching
+ // text/plain parts, the line ending must always be preserved
+ // and this is not possible without a binary encoding. See
+ // https://gitlab.gnome.org/GNOME/geary/-/issues/1001
//
- // XXX We should be setting the content encoding lazily
- // though because if sending via a MTA that supports 8-bit
- // or binary transfer modes, we can avoid using a content
- // encoding altogether.
- GMime.ContentEncoding encoding = BASE64;
- if (content_type.is_type("text", Mime.ContentType.WILDCARD)) {
- encoding = yield Utils.get_best_encoding(
- stream,
- GMime.EncodingConstraint.7BIT,
- cancellable
- );
- }
+ // TODO: The actual content encoding should be set based on
+ // the IMAP/SMTP server's supported encoding. For example, if
+ // 8-bit or binary is supported, then those should be used
+ // instead of Base64.
+ part.set_content_encoding(BASE64);
- part.set_content_encoding(encoding);
+ GMime.StreamMem stream = Utils.create_stream_mem(buffer);
part.set_content(
new GMime.DataWrapper.with_stream(
stream, GMime.ContentEncoding.BINARY
)
);
+
return part;
}
--
2.29.2

View File

@ -0,0 +1,31 @@
From b5abd3f9664c396ad57f177750973695c58e8b7f Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Mon, 28 Sep 2020 08:53:35 +1000
Subject: [PATCH 031/124] build: Fix build failure due to missing client API
Don't use client lib vala ags when building the web process extension,
since that will cause it to also write a VAPI with the same name as
the client lib, causing the build to fail if the web process side wins
that race.
Fixes #985
---
src/meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/meson.build b/src/meson.build
index 87760e74..225777e8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -72,7 +72,7 @@ web_process = library('geary-web-process',
webkit2gtk_web_extension,
],
include_directories: config_h_dir,
- vala_args: client_vala_args,
+ vala_args: geary_vala_args,
c_args: geary_c_args,
install: true,
install_dir: web_extensions_dir
--
2.29.2

View File

@ -0,0 +1,62 @@
From 836a9ad3847d42087c4daeec81e84b7b7936714a Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Mon, 28 Sep 2020 22:26:23 +1000
Subject: [PATCH 032/124] FormattedConversationData: Fix font settings being
ignored under Flatpak
Get the interface font from Gtk.Settings instead of GLib.Settings since
the latter can't actually access desktop settings under Flatpak.
Partial fix for #989
See also GNOME/glib#2213
---
.../formatted-conversation-data.vala | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/client/conversation-list/formatted-conversation-data.vala b/src/client/conversation-list/formatted-conversation-data.vala
index f2b635f8..b21a931f 100644
--- a/src/client/conversation-list/formatted-conversation-data.vala
+++ b/src/client/conversation-list/formatted-conversation-data.vala
@@ -100,6 +100,8 @@ public class FormattedConversationData : Geary.BaseObject {
public Geary.Email? preview { get; private set; default = null; }
private Application.Configuration config;
+
+ private Gtk.Settings? gtk;
private Pango.FontDescription font;
private Geary.App.Conversation? conversation = null;
@@ -115,13 +117,13 @@ public class FormattedConversationData : Geary.BaseObject {
Geary.Email preview,
Gee.List<Geary.RFC822.MailboxAddress> account_owner_emails) {
this.config = config;
+ this.gtk = Gtk.Settings.get_default();
this.conversation = conversation;
this.account_owner_emails = account_owner_emails;
this.use_to = conversation.base_folder.used_as.is_outgoing();
- this.font = Pango.FontDescription.from_string(
- this.config.gnome_interface.get_string("font-name")
- );
+ this.gtk.notify["gtk-font-name"].connect(this.update_font);
+ update_font();
// Load preview-related data.
update_date_string();
@@ -472,4 +474,12 @@ public class FormattedConversationData : Geary.BaseObject {
return ink_rect;
}
+ private void update_font() {
+ var name = "Cantarell 11";
+ if (this.gtk != null) {
+ name = this.gtk.gtk_font_name;
+ }
+ this.font = Pango.FontDescription.from_string(name);
+ }
+
}
--
2.29.2

View File

@ -0,0 +1,64 @@
From bfbc7f5d70f724010a9789ecefca6c1aa9f22962 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Tue, 29 Sep 2020 23:01:38 +1000
Subject: [PATCH 033/124] client: Fix not all folders being displayed in
additional main windows
Application.MainWindow: Sort folders already available in an account
by path so that FolderListTree is able to add them all successfully.
Application.FolderContext: Implement Gee.Comparable so instances can
be sorted.
Fixes #1004
---
src/client/application/application-folder-context.vala | 7 ++++++-
src/client/application/application-main-window.vala | 7 ++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/client/application/application-folder-context.vala b/src/client/application/application-folder-context.vala
index b85c901b..4ed47cf5 100644
--- a/src/client/application/application-folder-context.vala
+++ b/src/client/application/application-folder-context.vala
@@ -9,7 +9,8 @@
/**
* Collects application state related to a single folder.
*/
-public class Application.FolderContext : Geary.BaseObject {
+public class Application.FolderContext : Geary.BaseObject,
+ Gee.Comparable<FolderContext> {
/** Specifies different kinds of displayable email counts. */
@@ -41,6 +42,10 @@ public class Application.FolderContext : Geary.BaseObject {
update();
}
+ public int compare_to(FolderContext other) {
+ return this.folder.path.compare_to(other.folder.path);
+ }
+
private void update() {
this.display_name = Util.I18n.to_folder_display_name(this.folder);
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 73b50e33..47749019 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1010,7 +1010,12 @@ public class Application.MainWindow :
to_add.commands.undone.connect(on_command_undo);
to_add.commands.redone.connect(on_command_redo);
- add_folders(to_add.get_folders());
+ // Sort the folders so FolderListTree adds them all
+ // correctly
+ var added = new Gee.TreeSet<FolderContext>();
+ added.add_all(to_add.get_folders());
+
+ add_folders(added);
this.accounts.add(to_add);
}
}
--
2.29.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
From aaa2934acfb53243d21a4a68ac6486951ae4b045 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 3 Oct 2020 21:06:43 +1000
Subject: [PATCH 035/124] meson_options.txt: Update to use meson best practices
and clean up
Convert to use meson features for features. Reorganise and rename
options for consistency. Make descriptions a bit less redundant.
---
desktop/meson.build | 2 +-
meson.build | 27 +++++++---------
meson_options.txt | 71 ++++++++++++++++++++++++------------------
src/engine/meson.build | 2 +-
src/meson.build | 4 +--
test/meson.build | 2 +-
6 files changed, 57 insertions(+), 51 deletions(-)
diff --git a/desktop/meson.build b/desktop/meson.build
index f0c8a660..712db358 100644
--- a/desktop/meson.build
+++ b/desktop/meson.build
@@ -77,7 +77,7 @@ endif
# Contractor file (Elementary OS)
#
-if install_contractor_file
+if get_option('contractor').enabled()
# Call msgfmt manually since gettext won't otherwise translate the
# Description field. See merge req !50.
msgfmt = find_program('msgfmt')
diff --git a/meson.build b/meson.build
index 240eacaa..82f0439d 100644
--- a/meson.build
+++ b/meson.build
@@ -4,13 +4,6 @@ project('geary', [ 'vala', 'c' ],
meson_version: '>= 0.50',
)
-# Build-time configuration options
-enable_valadoc = get_option('valadoc')
-install_contractor_file = get_option('contractor')
-iso_3166_xml = get_option('iso_3166_xml')
-iso_639_xml = get_option('iso_639_xml')
-reference_tracking = get_option('ref_tracking')
-
# Build type
if get_option('profile') == 'development'
profile = '.Devel'
@@ -92,13 +85,13 @@ libpeas_gtk = dependency('libpeas-gtk-1.0', version: '>= 1.24.0')
libsecret = dependency('libsecret-1', version: '>= 0.11')
libsoup = dependency('libsoup-2.4', version: '>= 2.48')
libunwind_dep = dependency(
- 'libunwind', version: '>= 1.1', required: not get_option('libunwind_optional')
+ 'libunwind', version: '>= 1.1', required: get_option('libunwind')
)
libunwind_generic_dep = dependency(
- 'libunwind-generic', version: '>= 1.1', required: not get_option('libunwind_optional')
+ 'libunwind-generic', version: '>= 1.1', required: get_option('libunwind')
)
libxml = dependency('libxml-2.0', version: '>= 2.7.8')
-libytnef = dependency('libytnef', version: '>= 1.9.3', required: get_option('tnef-support'))
+libytnef = dependency('libytnef', version: '>= 1.9.3', required: get_option('tnef'))
posix = valac.find_library('posix')
webkit2gtk_web_extension = dependency('webkit2gtk-web-extension-4.0', version: '>=' + target_webkit)
@@ -153,27 +146,31 @@ endif
# Build glue
#
+valadoc = find_program('valadoc', required: get_option('valadoc'))
+
vala_unit_proj = subproject(
'vala-unit',
default_options: [
'install=false',
- 'valadoc=@0@'.format(enable_valadoc)
+ 'valadoc=@0@'.format(valadoc.found())
]
)
vala_unit_dep = vala_unit_proj.get_variable('vala_unit_dep')
-if enable_valadoc
- valadoc = find_program('valadoc')
-endif
-
# Language detection
+
iso_codes_dir = iso_codes.get_pkgconfig_variable('prefix')/'share'/'xml'/'iso-codes'
+
+iso_639_xml = get_option('iso_639_xml')
if iso_639_xml == ''
iso_639_xml = iso_codes_dir / 'iso_639.xml'
endif
+
+iso_3166_xml = get_option('iso_3166_xml')
if iso_3166_xml == ''
iso_3166_xml = iso_codes_dir / 'iso_3166.xml'
endif
+
files(iso_639_xml, iso_3166_xml) # Check to make sure these exist
# Post-install scripts
diff --git a/meson_options.txt b/meson_options.txt
index a18438d4..968c2541 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,11 +1,18 @@
#
# General build options
#
+
option(
- 'contractor',
- type: 'boolean',
- value: false,
- description: 'Whether to install the contractor file (Elementary OS-specific).'
+ 'profile',
+ type: 'combo',
+ value: 'default',
+ choices: ['default','development','beta'],
+ description: 'Specifies the application type to be built'
+)
+option(
+ 'revno',
+ type: 'string',
+ description: 'Custom revision string (default extracted from "git describe")'
)
option(
'iso_639_xml',
@@ -19,41 +26,43 @@ option(
value: '',
description: 'Full path to the ISO 3166 XML file.'
)
-option(
- 'libunwind_optional',
- type: 'boolean',
- value: false,
- description: 'Determines if libunwind is required.'
-)
-option(
- 'tnef-support',
- type: 'boolean',
- value: true,
- description: 'Whether to support TNEF attachments (requires libytnef).'
-)
option(
'valadoc',
- type: 'boolean',
- value: false,
- description: 'Whether to build the documentation (requires valadoc).'
+ type: 'feature',
+ value: 'auto',
+ description: 'Build API documentation'
)
+#
# Development options
+#
+
option(
- 'profile',
- type: 'combo',
- value: 'default',
- choices: ['default','development','beta'],
- description: 'Specifies the application type to be built'
+ 'ref_tracking',
+ type: 'feature',
+ value: 'disabled',
+ description: 'Enable Geary.BaseObject reference tracking'
+)
+
+#
+# Optional features
+#
+
+option(
+ 'contractor',
+ type: 'feature',
+ value: 'disabled',
+ description: 'Install an Elementary OS a contractor file'
)
option(
- 'ref_tracking',
- type: 'boolean',
- value: false,
- description: 'Whether to use explicit reference tracking.'
+ 'libunwind',
+ type: 'feature',
+ value: 'enabled',
+ description: 'Use libunwind for back traces in problem reports.'
)
option(
- 'revno',
- type: 'string',
- description: 'Custom revision string (default extracted from "git describe")'
+ 'tnef',
+ type: 'feature',
+ value: 'enabled',
+ description: 'Support Microsoft-proprietary TNEF attachments.'
)
diff --git a/src/engine/meson.build b/src/engine/meson.build
index 0efd773e..1133f7b8 100644
--- a/src/engine/meson.build
+++ b/src/engine/meson.build
@@ -349,7 +349,7 @@ if libunwind_dep.found()
]
endif
-if get_option('tnef-support')
+if libytnef.found()
engine_dependencies += libytnef
engine_vala_args += [
'-D', 'WITH_TNEF_SUPPORT'
diff --git a/src/meson.build b/src/meson.build
index 225777e8..14f08c18 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -33,7 +33,7 @@ endif
# Symbols for valac's preprocessor must be defined as compiler args,
# not in the code or in config.h
-if reference_tracking
+if get_option('ref_tracking').enabled()
geary_vala_args += [ '--define=REF_TRACKING' ]
endif
@@ -157,7 +157,7 @@ foreach dir : valadoc_vapi_dirs
valadoc_vapidir_args += '--vapidir=@0@'.format(dir)
endforeach
-if enable_valadoc
+if valadoc.found()
docs = custom_target('valadoc',
build_by_default: true,
depends: [client_lib, engine_lib],
diff --git a/test/meson.build b/test/meson.build
index fe3040dd..a32b2a82 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -118,7 +118,7 @@ test_engine_dependencies += engine_dependencies
test_engine_vala_args = geary_vala_args
-if get_option('tnef-support')
+if libytnef.found()
test_engine_dependencies += libytnef
test_engine_vala_args += [
'-D', 'WITH_TNEF_SUPPORT'
--
2.29.2

View File

@ -0,0 +1,143 @@
From 23bd2507a7512664802db41b88ad375298d6b7d0 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 3 Oct 2020 21:59:01 +1000
Subject: [PATCH 036/124] build: Update how build profiles are handled
Default to development build profile if a `.git` directory exists, else
error out of build configuration.
This make `auto` the default build profile and if set and a `.git`
directory is present default to `development`, else raise an error.
Add some docs to INSTALL describing build profiles and update how they
are used in the source to match.
---
INSTALL | 26 +++++++++++++++--
meson.build | 28 +++++++++++++------
meson_options.txt | 8 ++++--
.../application/application-main-window.vala | 2 +-
4 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/INSTALL b/INSTALL
index 6572303f..e445a409 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,6 +15,28 @@ repository:
A convenience Makefile for development only is also provided. To use
it, simply invoke make from the top-level directory.
+Build profiles
+--------------
+
+Geary can be built using a number of different build profiles, which
+determine things like the application id, the location of stored data,
+the name of the application and other visual elements that distinguish
+release builds from other types.
+
+These can be set at build configuration time using the Meson `setup`
+and `configure` commands, using the standard `-Dprofile=…` option. See
+the `profile` option in `meson_options.txt` for the current list of
+supported types.
+
+Maintainers must select the `release` build profile when packaging
+non-test release builds, otherwise Geary will using branding and data
+locations intended for development only.
+
+Note that setting the profile does not alter such things as cmopiler
+options, use the standard Meson `--buildtype` argument for that.
+
+If built from
+
Dependencies
------------
@@ -94,5 +116,5 @@ the initial configuration step:
meson --prefix=/usr -C build
---
-Copyright 2016 Software Freedom Conservancy Inc.
-Copyright 2018 Michael Gratton <mike@vee.net>
+Copyright © 2016 Software Freedom Conservancy Inc.
+Copyright © 2018-2020 Michael Gratton <mike@vee.net>
diff --git a/meson.build b/meson.build
index 82f0439d..1dc9e3aa 100644
--- a/meson.build
+++ b/meson.build
@@ -4,16 +4,26 @@ project('geary', [ 'vala', 'c' ],
meson_version: '>= 0.50',
)
-# Build type
-if get_option('profile') == 'development'
- profile = '.Devel'
+# Determine the type of build
+profile = get_option('profile')
+appid_suffix = ''
+name_suffix = ''
+if profile == 'auto'
+ if run_command('[', '-d', '.git', ']').returncode() == 0
+ profile = 'development'
+ else
+ error('No build profile specified, see INSTALL')
+ endif
+endif
+
+if profile == 'development'
+ appid_suffix = '.Devel'
name_suffix = ' (Development)'
-elif get_option('profile') == 'beta'
- profile = '.Beta'
+elif profile == 'beta'
+ appid_suffix = '.Beta'
name_suffix = ' (Beta)'
-else
- profile = ''
- name_suffix = ''
+elif profile != 'release'
+ error('Unknown build profile specified, see INSTALL')
endif
# Configurable install dirs
@@ -120,7 +130,7 @@ libmessagingmenu_dep = dependency('messaging-menu', version: '>= 12.10', require
#
# Build variables
-geary_id = 'org.gnome.Geary@0@'.format(profile)
+geary_id = 'org.gnome.Geary@0@'.format(appid_suffix)
geary_version = meson.project_version()
revno = get_option('revno')
if revno == ''
diff --git a/meson_options.txt b/meson_options.txt
index 968c2541..fcb8b9bf 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -5,8 +5,12 @@
option(
'profile',
type: 'combo',
- value: 'default',
- choices: ['default','development','beta'],
+ choices: [
+ 'auto',
+ 'development',
+ 'beta',
+ 'release'
+ ],
description: 'Specifies the application type to be built'
)
option(
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 47749019..aba5fa26 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -487,7 +487,7 @@ public class Application.MainWindow :
load_config(application.config);
restore_saved_window_state();
- if (_PROFILE != "") {
+ if (_PROFILE != "release") {
this.get_style_context().add_class("devel");
}
--
2.29.2

View File

@ -0,0 +1,43 @@
From 456b6cd55ab4feb5c4c079df2b6aacbcfb69f246 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 3 Oct 2020 22:04:12 +1000
Subject: [PATCH 037/124] Application.Client: Sort external const
alphabetically
---
src/client/application/application-client.vala | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala
index 2bf4e094..7a450fa2 100644
--- a/src/client/application/application-client.vala
+++ b/src/client/application/application-client.vala
@@ -7,18 +7,18 @@
*/
// Defined by CMake build script.
-extern const string _INSTALL_PREFIX;
-extern const string _GSETTINGS_DIR;
-extern const string _WEB_EXTENSIONS_DIR;
-extern const string _PLUGINS_DIR;
-extern const string _SOURCE_ROOT_DIR;
-extern const string _BUILD_ROOT_DIR;
extern const string GETTEXT_PACKAGE;
extern const string _APP_ID;
+extern const string _BUILD_ROOT_DIR;
+extern const string _GSETTINGS_DIR;
+extern const string _INSTALL_PREFIX;
extern const string _NAME_SUFFIX;
+extern const string _PLUGINS_DIR;
extern const string _PROFILE;
-extern const string _VERSION;
extern const string _REVNO;
+extern const string _SOURCE_ROOT_DIR;
+extern const string _VERSION;
+extern const string _WEB_EXTENSIONS_DIR;
/**
--
2.29.2

View File

@ -0,0 +1,317 @@
From c240884f521da7356d9c5792a7568abc2dba5b38 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 3 Oct 2020 23:31:35 +1000
Subject: [PATCH 038/124] Rename INSTALLING to BUILDING.md
Renamed since most people want to know how to build Geary when they
get its source, not install it. Use MD extension to get formatting in
gitlab.
---
BUILDING.md | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++
INSTALL | 120 ---------------------------------------------------
README.md | 9 ++--
meson.build | 4 +-
4 files changed, 129 insertions(+), 126 deletions(-)
create mode 100644 BUILDING.md
delete mode 100644 INSTALL
diff --git a/BUILDING.md b/BUILDING.md
new file mode 100644
index 00000000..c00b1901
--- /dev/null
+++ b/BUILDING.md
@@ -0,0 +1,122 @@
+Building and running Geary
+==========================
+
+Geary uses the [Meson](http://mesonbuild.com) and
+[Ninja](https://ninja-build.org) build systems. You will need these
+and a number of other development libraries installed to build
+Geary. See the Dependencies section below for a list of packages to
+install.
+
+Building, running, tests and documentation
+------------------------------------------
+
+To build Geary, run the following commands from the top-level
+directory of the source code repository:
+
+```
+meson build
+ninja -C build
+```
+
+Once built, Geary can be run directly from the build directory without
+being installed:
+
+```
+./build/src/geary
+```
+
+Note that certain desktop integration (such as being listed in an
+application menu) requires full installation to work correctly.
+
+To run the unit tests, use the Meson `test` command:
+
+```
+meson test -C build
+```
+
+API documentation will be built if `valadoc` is installed.
+
+Consult the Meson documentation for information about configuring the
+build, installing, and so on.
+
+Build profiles
+--------------
+
+Geary can be built using a number of different build profiles, which
+determine things like the application id, the location of stored data,
+the name of the application, icon and other visual elements.
+
+These can be set at build configuration time using the Meson `setup`
+and `configure` commands, using the standard `-Dprofile=…` option. See
+the `profile` option in `meson_options.txt` for the current list of
+supported types.
+
+Maintainers must use the `release` build profile when packaging Geary,
+otherwise when run it will use branding and data locations intended
+for development only.
+
+Note that setting the profile does not alter such things as compiler
+options, use the standard Meson `--buildtype` argument for that.
+
+Consult the Meson documentation for more information about configuring
+options.
+
+Dependencies
+------------
+
+Building Geary requires the following major libraries and tools:
+
+ * GTK+ 3
+ * WebKitGTK+ 2
+ * SQLite 3
+ * Vala
+
+See the `meson.build` file in the top-level directory for the complete
+list of required dependencies and minimum versions.
+
+Geary also requires SQLite to be built with the compiler flag
+`-DSQLITE_ENABLE_FTS3`.
+
+All required libraries and tools are available from major Linux
+distribution's package repositories:
+
+Installing dependencies on Fedora
+---------------------------------
+
+Install them by running this command:
+
+```
+sudo dnf install meson vala desktop-file-utils enchant2-devel \
+ folks-devel gcr-devel glib2-devel gmime30-devel \
+ gnome-online-accounts-devel gspell-devel gsound-devel \
+ gtk3-devel iso-codes-devel itstool json-glib-devel \
+ libappstream-glib-devel libgee-devel libhandy1-devel \
+ libpeas-devel libsecret-devel libunwind-devel libxml2-devel \
+ libytnef-devel sqlite-devel webkitgtk4-devel
+```
+
+Installing dependencies on Ubuntu/Debian
+----------------------------------------
+
+Install them by running this command:
+
+```
+sudo apt-get install meson build-essential valac \
+ desktop-file-utils iso-codes gettext itstool \
+ libappstream-glib-dev libenchant-2-dev libfolks-dev \
+ libgcr-3-dev libgee-0.8-dev libglib2.0-dev libgmime-3.0-dev \
+ libgoa-1.0-dev libgspell-1-dev libgsound-dev libgtk-3-dev \
+ libjson-glib-dev libhandy-1-dev libpeas-dev libsecret-1-dev \
+ libsqlite3-dev libunwind-dev libwebkit2gtk-4.0-dev libxml2-dev \
+ libytnef0-dev
+```
+
+And for Ubuntu Messaging Menu integration:
+
+```
+sudo apt-get install libmessaging-menu-dev
+```
+
+---
+Copyright © 2016 Software Freedom Conservancy Inc.
+Copyright © 2018-2020 Michael Gratton <mike@vee.net>
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index e445a409..00000000
--- a/INSTALL
+++ /dev/null
@@ -1,120 +0,0 @@
-Building & Installing Geary
-===========================
-
-Building
---------
-
-Geary uses the Meson <http://mesonbuild.com> and Ninja
-<https://ninja-build.org> build systems. To build Geary, run the
-following commands from the top-level directory of the source code
-repository:
-
- meson build
- ninja -C build
-
-A convenience Makefile for development only is also provided. To use
-it, simply invoke make from the top-level directory.
-
-Build profiles
---------------
-
-Geary can be built using a number of different build profiles, which
-determine things like the application id, the location of stored data,
-the name of the application and other visual elements that distinguish
-release builds from other types.
-
-These can be set at build configuration time using the Meson `setup`
-and `configure` commands, using the standard `-Dprofile=…` option. See
-the `profile` option in `meson_options.txt` for the current list of
-supported types.
-
-Maintainers must select the `release` build profile when packaging
-non-test release builds, otherwise Geary will using branding and data
-locations intended for development only.
-
-Note that setting the profile does not alter such things as cmopiler
-options, use the standard Meson `--buildtype` argument for that.
-
-If built from
-
-Dependencies
-------------
-
-Building Geary requires the following major libraries and tools:
-
- * GTK+ 3
- * WebKitGTK+ 2
- * SQLite 3
- * Vala
-
-See the `meson.build` file in the top-level directory for the complete
-list of required dependencies and minimum versions.
-
-Geary also requires SQLite to be built with the compiler flag
-`-DSQLITE_ENABLE_FTS3`.
-
-All required libraries and tools are available from major Linux
-distribution's package repositories:
-
-Installing dependencies on Fedora
----------------------------------
-
-Install them by running this command:
-
- sudo dnf install meson vala desktop-file-utils enchant2-devel \
- folks-devel gcr-devel glib2-devel gmime30-devel \
- gnome-online-accounts-devel gspell-devel gsound-devel \
- gtk3-devel iso-codes-devel itstool json-glib-devel \
- libappstream-glib-devel libgee-devel libhandy1-devel \
- libpeas-devel libsecret-devel libunwind-devel libxml2-devel \
- libytnef-devel sqlite-devel webkitgtk4-devel
-
-Installing dependencies on Ubuntu/Debian
-----------------------------------------
-
-Install them by running this command:
-
- sudo apt-get install meson build-essential valac \
- desktop-file-utils iso-codes gettext itstool \
- libappstream-glib-dev libenchant-2-dev libfolks-dev \
- libgcr-3-dev libgee-0.8-dev libglib2.0-dev libgmime-3.0-dev \
- libgoa-1.0-dev libgspell-1-dev libgsound-dev libgtk-3-dev \
- libjson-glib-dev libhandy-1-dev libpeas-dev libsecret-1-dev \
- libsqlite3-dev libunwind-dev libwebkit2gtk-4.0-dev libxml2-dev \
- libytnef0-dev
-
-And for Ubuntu Messaging Menu integration:
-
- sudo apt-get install libmessaging-menu-dev
-
-Running
--------
-
-If you wish to try Geary before installing it, you may execute it directly
-from its build directory:
-
- ./build/src/geary
-
-Note that certain desktop integration (such as being listed in an
-application menu) requires full installation.
-
-Installation
-------------
-
-After Geary has built, install it by invoking the install target:
-
- ninja -C build install
-
-After installation, it can be uninstalled in the same way:
-
- ninja -C build uninstall
-
-By default, Geary will install under /usr/local. To install to a
-different directory, set pass the --prefix to meson when performing
-the initial configuration step:
-
- meson --prefix=/usr -C build
-
----
-Copyright © 2016 Software Freedom Conservancy Inc.
-Copyright © 2018-2020 Michael Gratton <mike@vee.net>
diff --git a/README.md b/README.md
index d99cd8aa..73c4e595 100644
--- a/README.md
+++ b/README.md
@@ -17,11 +17,12 @@ for more information.
![Geary displaying a conversation](https://wiki.gnome.org/Apps/Geary?action=AttachFile&amp;do=get&amp;target=geary-3-32-main-window.png)
-Installation & Licensing
-------------------------
+Building & Licensing
+--------------------
-Please consult the [INSTALL](./INSTALL) and [COPYING](./COPYING) files
-for more information.
+Please consult the [BUILDING.md](./BUILDING.md) and
+[COPYING](./COPYING) files for more information about building Geary
+and the licence granted by its copyright holders for redistribution.
Getting in Touch
----------------
diff --git a/meson.build b/meson.build
index 1dc9e3aa..54bccfec 100644
--- a/meson.build
+++ b/meson.build
@@ -12,7 +12,7 @@ if profile == 'auto'
if run_command('[', '-d', '.git', ']').returncode() == 0
profile = 'development'
else
- error('No build profile specified, see INSTALL')
+ error('No build profile specified, see BUILDING.md')
endif
endif
@@ -23,7 +23,7 @@ elif profile == 'beta'
appid_suffix = '.Beta'
name_suffix = ' (Beta)'
elif profile != 'release'
- error('Unknown build profile specified, see INSTALL')
+ error('Unknown build profile specified, see BUILDING.md')
endif
# Configurable install dirs
--
2.29.2

View File

@ -0,0 +1,43 @@
From 436c22a4ada3538f7a624c654557471ef0099530 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 3 Oct 2020 23:33:41 +1000
Subject: [PATCH 039/124] README.md: Minor improvements
---
README.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 73c4e595..6ffcb86b 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Geary: Send and receive email
Geary is an email application built around conversations, for the
GNOME 3 desktop. It allows you to read, find and send email with a
-straightforward, modern interface.
+straight-forward, modern interface.
Visit https://wiki.gnome.org/Apps/Geary for more information.
@@ -31,7 +31,8 @@ Getting in Touch
* Support and discussion: See the `geary` tag on [GNOME Discourse](https://discourse.gnome.org/tags/c/applications/7/geary)
* Matrix channel: [#geary:gnome.org](https://gnome.element.io/#/room/#geary:gnome.org)
-**Code Of Conduct**
+Code Of Conduct
+---------------
We follow the [Contributor Covenant](./code-of-conduct.md) as our
Code of Conduct. All communications in project spaces are expected to
@@ -48,5 +49,5 @@ Want to help improve Geary? Here are some ways to contribute:
* Donate: https://wiki.gnome.org/Apps/Geary/Donate
---
-Copyright 2016 Software Freedom Conservancy Inc.
-Copyright 2017-2020 Michael Gratton <mike@vee.net>
+Copyright © 2016 Software Freedom Conservancy Inc.
+Copyright © 2017-2020 Michael Gratton <mike@vee.net>
--
2.29.2

View File

@ -0,0 +1,582 @@
From d2a0694866dffb73c2f42471577fb96d05e69489 Mon Sep 17 00:00:00 2001
From: Kukuh Syafaat <kukuhsyafaat@gnome.org>
Date: Sun, 4 Oct 2020 08:34:11 +0000
Subject: [PATCH 041/124] Update Indonesian translation
---
po/id.po | 198 ++++++++++++++++++++++++++++---------------------------
1 file changed, 102 insertions(+), 96 deletions(-)
diff --git a/po/id.po b/po/id.po
index d01d1604..0e195fee 100644
--- a/po/id.po
+++ b/po/id.po
@@ -11,8 +11,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary mainline\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-08-18 09:04+0000\n"
-"PO-Revision-Date: 2020-08-21 23:43+0700\n"
+"POT-Creation-Date: 2020-08-27 12:37+0000\n"
+"PO-Revision-Date: 2020-08-28 22:22+0700\n"
"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
@@ -48,7 +48,7 @@ msgstr "Surel"
#: desktop/geary-autostart.desktop.in.in:5
#: desktop/org.gnome.Geary.appdata.xml.in.in:15
#: desktop/org.gnome.Geary.desktop.in.in:5
-#: src/client/application/application-client.vala:32
+#: src/client/application/application-client.vala:33
msgid "Send and receive email"
msgstr "Kirim dan terima surel"
@@ -813,143 +813,143 @@ msgstr "Berkas telah ada di \"%s\". Kalau ditimpa isi sebelumnya hilang."
msgid "_Replace"
msgstr "Timp_a"
-#: src/client/application/application-client.vala:33
+#: src/client/application/application-client.vala:34
msgid "Copyright 2016 Software Freedom Conservancy Inc."
msgstr "Hak Cipta 2016 Software Freedom Conservancy Inc."
-#: src/client/application/application-client.vala:34
+#: src/client/application/application-client.vala:35
msgid "Copyright 2016-2020 Geary Development Team."
msgstr "Hak Cipta 2016-2020 Tim Pengembang Geary."
-#: src/client/application/application-client.vala:36
+#: src/client/application/application-client.vala:37
msgid "Visit the Geary web site"
msgstr "Kunjungi situs web Geary"
#. / Command line option
-#: src/client/application/application-client.vala:96
+#: src/client/application/application-client.vala:97
msgid "Print debug logging"
msgstr "Cetak log awakutu"
#. / Command line option
-#: src/client/application/application-client.vala:99
+#: src/client/application/application-client.vala:100
msgid "Start with the main window hidden (deprecated)"
msgstr "Mulai Geary dengan jendela utama tersembunyi (usang)"
#. / Command line option
-#: src/client/application/application-client.vala:102
+#: src/client/application/application-client.vala:103
msgid "Enable WebKitGTK Inspector in web views"
msgstr "Fungsikan WebKitGTK Inspector dalam tilikan web"
#. / Command line option
-#: src/client/application/application-client.vala:105
+#: src/client/application/application-client.vala:106
msgid "Log conversation monitoring"
msgstr "Catat pemantauan percakapan"
#. / Command line option
-#: src/client/application/application-client.vala:108
+#: src/client/application/application-client.vala:109
msgid "Log IMAP network deserialization"
msgstr "Log deserialisasi jaringan IMAP"
#. / Command line option. "Normalization" can also be called
#. / "synchronization".
-#: src/client/application/application-client.vala:112
+#: src/client/application/application-client.vala:113
msgid "Log folder normalization"
msgstr "Catat normalisasi map"
#. / Command line option
-#: src/client/application/application-client.vala:115
+#: src/client/application/application-client.vala:116
msgid "Log IMAP network activity"
msgstr "Log aktivitas jaringan IMAP"
#. / Command line option. The IMAP replay queue is how changes
#. / on the server are replicated on the client. It could
#. / also be called the IMAP events queue.
-#: src/client/application/application-client.vala:120
+#: src/client/application/application-client.vala:121
msgid "Log IMAP replay queue"
msgstr "Catat antrian putar ulang IMAP"
#. / Command line option
-#: src/client/application/application-client.vala:123
+#: src/client/application/application-client.vala:124
msgid "Log SMTP network activity"
msgstr "Log aktivitas jaringan SMTP"
#. / Command line option
-#: src/client/application/application-client.vala:126
+#: src/client/application/application-client.vala:127
msgid "Log database queries (generates lots of messages)"
msgstr "Catat kuiri basis data (menimbulkan banyak pesan)"
#. / Command line option
-#: src/client/application/application-client.vala:129
+#: src/client/application/application-client.vala:130
msgid "Perform a graceful quit"
msgstr "Keluar secara anggun"
-#: src/client/application/application-client.vala:131
+#: src/client/application/application-client.vala:132
msgid "Open a new window"
msgstr "Buka suatu jendela baru"
#. / Command line option
-#: src/client/application/application-client.vala:134
+#: src/client/application/application-client.vala:135
msgid "Revoke all pinned TLS server certificates"
msgstr "Cabut semua sertifikat peladen TLS yang di-pin"
#. / Command line option
-#: src/client/application/application-client.vala:137
+#: src/client/application/application-client.vala:138
msgid "Display program version"
msgstr "Tampilkan versi program"
#. / Application runtime information label
-#: src/client/application/application-client.vala:261
+#: src/client/application/application-client.vala:262
msgid "Geary version"
msgstr "Versi Geary"
#. / Application runtime information label
-#: src/client/application/application-client.vala:263
+#: src/client/application/application-client.vala:264
msgid "Geary revision"
msgstr "Revisi Geary"
#. / Application runtime information label
-#: src/client/application/application-client.vala:265
+#: src/client/application/application-client.vala:266
msgid "GTK version"
msgstr "Versi GTK"
#. / Applciation runtime information label
-#: src/client/application/application-client.vala:272
+#: src/client/application/application-client.vala:273
msgid "GLib version"
msgstr "Versi GLib"
#. / Application runtime information label
-#: src/client/application/application-client.vala:279
+#: src/client/application/application-client.vala:280
msgid "WebKitGTK version"
msgstr "Versi WebKitGTK"
#. / Application runtime information label
-#: src/client/application/application-client.vala:286
+#: src/client/application/application-client.vala:287
msgid "Desktop environment"
msgstr "Lingkungan desktop"
#. Translators: This is the file type displayed for
#. attachments with unknown file types.
-#: src/client/application/application-client.vala:288
+#: src/client/application/application-client.vala:289
#: src/client/components/components-attachment-pane.vala:91
msgid "Unknown"
msgstr "Tak dikenal"
#. / Application runtime information label
-#: src/client/application/application-client.vala:318
+#: src/client/application/application-client.vala:293
msgid "Distribution name"
msgstr "Nama distribusi"
#. / Application runtime information label
-#: src/client/application/application-client.vala:323
+#: src/client/application/application-client.vala:298
msgid "Distribution release"
msgstr "Rilis distribusi"
#. / Application runtime information label
-#: src/client/application/application-client.vala:331
+#: src/client/application/application-client.vala:303
msgid "Installation prefix"
msgstr "Prefiks instalasi"
-#: src/client/application/application-client.vala:584
+#: src/client/application/application-client.vala:558
#, c-format
msgid "About %s"
msgstr "Tentang %s"
@@ -957,7 +957,7 @@ msgstr "Tentang %s"
#. Translators: add your name and email address to receive
#. credit in the About dialog For example: Yamada Taro
#. <yamada.taro@example.com>
-#: src/client/application/application-client.vala:588
+#: src/client/application/application-client.vala:562
msgid "translator-credits"
msgstr ""
"Andika Triwidada <andika@gmail.com>, 2012, 2013, 2016, 2017, 2019, 2020\n"
@@ -966,13 +966,13 @@ msgstr ""
#. / Warning printed to the console when a deprecated
#. / command line option is used.
-#: src/client/application/application-client.vala:1066
+#: src/client/application/application-client.vala:1046
msgid "The `--hidden` option is deprecated and will be removed in the future."
msgstr "Opsi '--hidden' usang dan akan dihapus di masa mendatang."
#. / Command line warning, string substitution
#. / is the given argument
-#: src/client/application/application-client.vala:1099
+#: src/client/application/application-client.vala:1079
#, c-format
msgid "Unrecognised program argument: “%s”"
msgstr "Argumen program tidak dikenal: \"%s\""
@@ -1079,12 +1079,12 @@ msgid_plural "Conversations un-labelled as %s"
msgstr[0] "Label %s dihapus dari percakapan"
msgstr[1] "Label %s dihapus dari percakapan"
-#: src/client/application/application-controller.vala:1297
+#: src/client/application/application-controller.vala:1320
#, c-format
msgid "Unable to open the database for %s"
msgstr "Tak bisa membuka basis data bagi %s"
-#: src/client/application/application-controller.vala:1298
+#: src/client/application/application-controller.vala:1321
#, c-format
msgid ""
"There was an error opening the local mail database for this account. This is "
@@ -1108,20 +1108,20 @@ msgstr ""
"Membangun ulang basis data akan menghancurkan semua surel lokal dan "
"lampirannya. <b>Surat pada peladen Anda tak akan terpengaruh.</b>"
-#: src/client/application/application-controller.vala:1300
+#: src/client/application/application-controller.vala:1323
msgid "_Rebuild"
msgstr "_Bangun Ulang"
-#: src/client/application/application-controller.vala:1300
+#: src/client/application/application-controller.vala:1323
msgid "E_xit"
msgstr "_Keluar"
-#: src/client/application/application-controller.vala:1310
+#: src/client/application/application-controller.vala:1333
#, c-format
msgid "Unable to rebuild database for “%s”"
msgstr "Tak bisa membangun ulang basis data bagi \"%s\""
-#: src/client/application/application-controller.vala:1311
+#: src/client/application/application-controller.vala:1334
#, c-format
msgid ""
"Error during rebuild:\n"
@@ -1134,34 +1134,34 @@ msgstr ""
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
-#: src/client/application/application-controller.vala:1478
+#: src/client/application/application-controller.vala:1501
#, c-format
msgid "Email sent to %s"
msgstr "Surel dikirim ke %s"
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
-#: src/client/application/application-controller.vala:2509
+#: src/client/application/application-controller.vala:2491
#, c-format
msgid "Email to %s queued for delivery"
msgstr "Surel ke %s diantrikan untuk pengiriman"
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
-#: src/client/application/application-controller.vala:2573
+#: src/client/application/application-controller.vala:2555
#, c-format
msgid "Email to %s saved"
msgstr "Surel ke %s disimpan"
#. / Translators: A label for an in-app notification.
-#: src/client/application/application-controller.vala:2588
-#: src/client/application/application-controller.vala:2646
+#: src/client/application/application-controller.vala:2570
+#: src/client/application/application-controller.vala:2628
msgid "Composer could not be restored"
msgstr "Penyusun tidak dapat dipulihkan"
#. / Translators: The label for an in-app notification. The
#. / string substitution is a list of recipients of the email.
-#: src/client/application/application-controller.vala:2631
+#: src/client/application/application-controller.vala:2613
#, c-format
msgid "Email to %s discarded"
msgstr "Surel ke %s dibuang"
@@ -1304,7 +1304,7 @@ msgstr "Pemeriksa"
#. / Translators: Title for Inspector logs pane
#. / Translators: Title for problem report dialog logs pane
#: src/client/components/components-inspector.vala:93
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:102
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:101
msgid "Logs"
msgstr "Log"
@@ -1312,21 +1312,21 @@ msgstr "Log"
#. / Translators: Title for problem report system information
#. / pane
#: src/client/components/components-inspector.vala:97
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:105
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:104
msgid "System"
msgstr "Sistem"
#. Button label for saving problem report information
#: src/client/components/components-inspector.vala:226
#: src/client/components/components-inspector.vala:229
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:221
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
-#: ui/problem-details-dialog.ui:42
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:220
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:223
+#: ui/problem-details-dialog.ui:47
msgid "Save As"
msgstr "Simpan sebagai"
#: src/client/components/components-inspector.vala:230
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:225
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
#: ui/accounts_editor_servers_pane.ui:17
msgid "Cancel"
msgstr "Batal"
@@ -1375,7 +1375,7 @@ msgid "Preferences"
msgstr "Preferensi"
#. / Translators: Preferences page title
-#: src/client/components/components-preferences-window.vala:252
+#: src/client/components/components-preferences-window.vala:250
msgid "Plugins"
msgstr "Pengaya"
@@ -1448,6 +1448,22 @@ msgstr "Lihat rincian teknis tentang kesalahan tersebut"
msgid "_Retry"
msgstr "_Ulangi"
+#: src/client/components/components-reflow-box.c:454
+msgid "Spacing"
+msgstr "Jarak"
+
+#: src/client/components/components-reflow-box.c:455
+msgid "Spacing between children"
+msgstr "Jarak antara anak"
+
+#: src/client/components/components-reflow-box.c:470
+msgid "Row spacing"
+msgstr "Jarak antar baris"
+
+#: src/client/components/components-reflow-box.c:471
+msgid "Spacing between rows of children"
+msgstr "Jarak antar baris anak "
+
#. / Translators: Search entry placeholder text
#: src/client/components/components-search-bar.vala:12
#: src/client/folder-list/folder-list-search-branch.vala:53
@@ -1551,7 +1567,7 @@ msgid "_OK"
msgstr "_OK"
#: src/client/components/stock.vala:19
-#: src/client/plugin/mail-merge/mail-merge.vala:388
+#: src/client/plugin/mail-merge/mail-merge.vala:392
#: ui/password-dialog.glade:196
msgid "_Cancel"
msgstr "Ba_tal"
@@ -1577,7 +1593,7 @@ msgid "_Help"
msgstr "_Bantuan"
#: src/client/components/stock.vala:26
-#: src/client/plugin/mail-merge/mail-merge.vala:387
+#: src/client/plugin/mail-merge/mail-merge.vala:391
#: ui/components-attachment-pane-menus.ui:7
msgid "_Open"
msgstr "_Buka"
@@ -2038,8 +2054,8 @@ msgstr "Terjadi galat saat memroses sertifikat peladen"
#. / Translators: Title for problem report dialog error
#. / information pane
#. Dialog title for displaying technical details of a problem. Same as the button that invokes it.
-#: src/client/dialogs/dialogs-problem-details-dialog.vala:100
-#: ui/problem-details-dialog.ui:12
+#: src/client/dialogs/dialogs-problem-details-dialog.vala:99
+#: ui/problem-details-dialog.ui:17
msgid "Details"
msgstr "Detail"
@@ -2091,12 +2107,6 @@ msgid_plural "%d results"
msgstr[0] "%d hasil"
msgstr[1] "%d hasil"
-#. Translators: This is an internal plugin so this name does not need
-#. to be tanslated
-#: src/client/plugin/desktop-notifications/desktop-notifications.plugin.desktop.in:6
-msgid "Desktop Notifications"
-msgstr "Pemberitahuan Desktop"
-
#. / Notification body when a message as been received
#. / and other unread messages have not been
#. / seen. First string substitution is the message
@@ -2194,17 +2204,11 @@ msgstr "Kirim"
#. Translators: Info bar button label for editing a draft
#. email
#: src/client/plugin/email-templates/email-templates.vala:305
-#: src/client/plugin/mail-merge/mail-merge.vala:332
+#: src/client/plugin/mail-merge/mail-merge.vala:336
#: src/client/plugin/special-folders/special-folders.vala:187
msgid "Edit"
msgstr "Sunting"
-#. Translators: This is an internal plugin so this name does not need
-#. to be tanslated
-#: src/client/plugin/folder-highlight/folder-highlight.plugin.desktop.in:6
-msgid "Folder Highlight"
-msgstr "Sorot Folder"
-
#. / Translators: Menu item label for invoking mail
#. / merge in composer
#. / Translators: File chooser title after invoking mail
@@ -2212,9 +2216,9 @@ msgstr "Sorot Folder"
#. Translators: The name of the folder used to
#. display merged email
#: src/client/plugin/mail-merge/mail-merge.plugin.desktop.in:5
-#: src/client/plugin/mail-merge/mail-merge.vala:284
-#: src/client/plugin/mail-merge/mail-merge.vala:385
-#: src/client/plugin/mail-merge/mail-merge.vala:484
+#: src/client/plugin/mail-merge/mail-merge.vala:288
+#: src/client/plugin/mail-merge/mail-merge.vala:389
+#: src/client/plugin/mail-merge/mail-merge.vala:488
msgid "Mail Merge"
msgstr "Gabungan Surat"
@@ -2237,31 +2241,33 @@ msgstr "Jeda"
#. Translators: Info bar description for the mail merge
#. folder. The first string substitution the number of email
#. already sent, the second is the total number to send.
-#: src/client/plugin/mail-merge/mail-merge.vala:239
+#: src/client/plugin/mail-merge/mail-merge.vala:240
#, c-format
msgid "Sent %u of %u"
-msgstr "Terkirim %u dari %u"
+msgid_plural "Sent %u of %u"
+msgstr[0] "Terkirim %u dari %u"
+msgstr[1] "Terkirim %u dari %u"
#. Translators: Infobar status label for an email mail merge
#. template
-#: src/client/plugin/mail-merge/mail-merge.vala:320
+#: src/client/plugin/mail-merge/mail-merge.vala:324
msgid "Mail merge template"
msgstr "Templat gabungan surat"
#. Translators: Info bar button label for performing a
#. mail-merge on an email template
-#: src/client/plugin/mail-merge/mail-merge.vala:324
+#: src/client/plugin/mail-merge/mail-merge.vala:328
msgid "Merge"
msgstr "Gabung"
#. / Translators: Action bar menu button label for
#. / mail-merge plugin
-#: src/client/plugin/mail-merge/mail-merge.vala:373
+#: src/client/plugin/mail-merge/mail-merge.vala:377
msgid "Insert field"
msgstr "Sisipkan bidang"
#. / Translators: File chooser filer label
-#: src/client/plugin/mail-merge/mail-merge.vala:392
+#: src/client/plugin/mail-merge/mail-merge.vala:396
msgid "Comma separated values (CSV)"
msgstr "Nilai yang dipisahkan koma (CSV)"
@@ -2278,12 +2284,6 @@ msgstr "Menampilkan pemberitahuan Menu Perpesanan Unity atas surat baru"
msgid "%s — New Messages"
msgstr "%s - Pesan Baru"
-#. Translators: This is an internal plugin so this name does not need
-#. to be tanslated.
-#: src/client/plugin/notification-badge/notification-badge.plugin.desktop.in:6
-msgid "Notification Badge"
-msgstr "Lencana Pemberitahuan"
-
#: src/client/plugin/sent-sound/sent-sound.plugin.desktop.in:4
msgid "Sent Sound"
msgstr "Suara Terkirim"
@@ -2292,12 +2292,6 @@ msgstr "Suara Terkirim"
msgid "Plays the desktop sent-mail sound when an email is sent"
msgstr "Memutar suara surat-terkirim desktopk etika surel dikirim"
-#. Translators: This is an internal plugin so this name does not need
-#. to be tanslated.
-#: src/client/plugin/special-folders/special-folders.plugin.desktop.in:6
-msgid "Special Folders"
-msgstr "Folder Khusus"
-
#. Translators: Info bar button label for emptying
#. trash/spam folders
#: src/client/plugin/special-folders/special-folders.vala:167
@@ -3130,7 +3124,7 @@ msgstr "Buka lampiran yang dipilih"
msgid "Save _All"
msgstr "Simpan Semu_a"
-#: ui/components-inspector-error-view.ui:33
+#: ui/components-inspector-error-view.ui:31
msgid ""
"If the problem is serious or persists, please save and send these details to "
"one of the <a href=\"https://wiki.gnome.org/Apps/Geary/Contact\">contact "
@@ -3142,13 +3136,13 @@ msgstr ""
"lampirkan ke sebuah <a href=\"https://wiki.gnome.org/Apps/Geary/ReportingABug"
"\">laporan kutu baru</a>."
-#: ui/components-inspector-error-view.ui:49
+#: ui/components-inspector-error-view.ui:47
msgid "Details:"
msgstr "Detail:"
#. Tooltip for inspector button
#. Tooltip for problem report button
-#: ui/components-inspector.ui:19 ui/problem-details-dialog.ui:19
+#: ui/components-inspector.ui:19 ui/problem-details-dialog.ui:24
msgid "Search for matching log entries"
msgstr "Cari entri log yang cocok"
@@ -3164,13 +3158,13 @@ msgstr "Tambahkan entri penanda ke log"
#. Tooltip for inspector button
#. Tooltip for problem report button
-#: ui/components-inspector.ui:81 ui/problem-details-dialog.ui:46
+#: ui/components-inspector.ui:81 ui/problem-details-dialog.ui:51
msgid "Save logs entries and details"
msgstr "Simpan entri log dan rincian"
#. Tooltip for inspector button
#. Tooltip for problem report button
-#: ui/components-inspector.ui:101 ui/problem-details-dialog.ui:62
+#: ui/components-inspector.ui:101 ui/problem-details-dialog.ui:67
msgid "Copy to clipboard"
msgstr "Salin ke papan klip"
@@ -3731,6 +3725,18 @@ msgstr "Otentik_asikan"
msgid "Geary update in progress…"
msgstr "Pemutakhiran Geary sedang berlangsung…"
+#~ msgid "Desktop Notifications"
+#~ msgstr "Pemberitahuan Desktop"
+
+#~ msgid "Folder Highlight"
+#~ msgstr "Sorot Folder"
+
+#~ msgid "Notification Badge"
+#~ msgstr "Lencana Pemberitahuan"
+
+#~ msgid "Special Folders"
+#~ msgstr "Folder Khusus"
+
#~ msgid "Displays desktop notifications when new email is delivered"
#~ msgstr "Menampilkan pemberitahuan desktop ketika ada surat baru terkirim"
--
2.29.2

View File

@ -0,0 +1,23 @@
From d5512753459d6c1324fae48b1e6fc08526515932 Mon Sep 17 00:00:00 2001
From: Jordi Mas <jmas@softcatala.org>
Date: Wed, 7 Oct 2020 08:14:38 +0200
Subject: [PATCH 043/124] Fix accute in Catalan translation
---
help/ca/ca.po | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/help/ca/ca.po b/help/ca/ca.po
index ae243358..dbacd7ea 100644
--- a/help/ca/ca.po
+++ b/help/ca/ca.po
@@ -1508,5 +1508,5 @@ msgid ""
msgstr ""
"En el mode text pla, el text es justificarà automàticament mitjançant salts "
"de línies de manera que no tinguin més de 74 caràcters d'ample i, el text "
-"sagnat es justificarà i se citarà utilitzant el caràcter \"&gt,\" per a cada"
+"sagnat es justificarà i se citarà utilitzant el caràcter \"&gt;\" per a cada"
" nivell de cita."
--
2.29.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
From a1f74d24ff11869f108dc42016a648ce45999b70 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 16 Jan 2020 13:28:13 +0100
Subject: [PATCH 045/124] Drop saving the paned width
This is needed to replace GtkPaned by HdyLeaflet. This breaks syncing
the size of the headerbar with the rest of the window, it will be fixed
in a later commit.
---
desktop/org.gnome.Geary.gschema.xml | 24 -------------------
.../application-configuration.vala | 22 -----------------
.../application/application-main-window.vala | 17 -------------
src/client/components/main-toolbar.vala | 9 -------
4 files changed, 72 deletions(-)
diff --git a/desktop/org.gnome.Geary.gschema.xml b/desktop/org.gnome.Geary.gschema.xml
index bbbadc36..9850dffd 100644
--- a/desktop/org.gnome.Geary.gschema.xml
+++ b/desktop/org.gnome.Geary.gschema.xml
@@ -21,24 +21,6 @@
<description>The last recorded height of the application window.</description>
</key>
- <key name="folder-list-pane-position" type="i">
- <default>100</default>
- <summary>Position of folder list pane</summary>
- <description>Position of the folder list Paned grabber.</description>
- </key>
-
- <key name="folder-list-pane-position-horizontal" type="i">
- <default>-1</default>
- <summary>Position of folder list pane when horizontal</summary>
- <description>Position of the folder list Paned grabber in the horizontal orientation.</description>
- </key>
-
- <key name="folder-list-pane-position-vertical" type="i">
- <default>200</default>
- <summary>Position of folder list pane when vertical</summary>
- <description>Position of the folder list Paned grabber in the vertical orientation.</description>
- </key>
-
<key name="folder-list-pane-horizontal" type="b">
<default>true</default>
<summary>Orientation of the folder list pane</summary>
@@ -51,12 +33,6 @@
<description>True if the formatting toolbar in the composer is shown.</description>
</key>
- <key name="messages-pane-position" type="i">
- <default>250</default>
- <summary>Position of message list pane</summary>
- <description>Position of the message list Paned grabber.</description>
- </key>
-
<key name="autoselect" type="b">
<default>true</default>
<summary>Autoselect next message</summary>
diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala
index 00c359c9..51a11bbf 100644
--- a/src/client/application/application-configuration.vala
+++ b/src/client/application/application-configuration.vala
@@ -20,11 +20,7 @@ public class Application.Configuration : Geary.BaseObject {
public const string CONVERSATION_VIEWER_ZOOM_KEY = "conversation-viewer-zoom";
public const string DISPLAY_PREVIEW_KEY = "display-preview";
public const string FOLDER_LIST_PANE_HORIZONTAL_KEY = "folder-list-pane-horizontal";
- public const string FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY = "folder-list-pane-position-horizontal";
- public const string FOLDER_LIST_PANE_POSITION_KEY = "folder-list-pane-position";
- public const string FOLDER_LIST_PANE_POSITION_VERTICAL_KEY = "folder-list-pane-position-vertical";
public const string FORMATTING_TOOLBAR_VISIBLE = "formatting-toolbar-visible";
- public const string MESSAGES_PANE_POSITION_KEY = "messages-pane-position";
public const string OPTIONAL_PLUGINS = "optional-plugins";
public const string SEARCH_STRATEGY_KEY = "search-strategy";
public const string SINGLE_KEY_SHORTCUTS = "single-key-shortcuts";
@@ -90,19 +86,6 @@ public class Application.Configuration : Geary.BaseObject {
get { return settings.get_boolean(WINDOW_MAXIMIZE_KEY); }
}
- public int folder_list_pane_position_old {
- get { return settings.get_int(FOLDER_LIST_PANE_POSITION_KEY); }
- }
-
- public int folder_list_pane_position_horizontal {
- get { return settings.get_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY); }
- set { settings.set_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY, value); }
- }
-
- public int folder_list_pane_position_vertical {
- get { return settings.get_int(FOLDER_LIST_PANE_POSITION_VERTICAL_KEY); }
- }
-
public bool folder_list_pane_horizontal {
get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); }
}
@@ -112,11 +95,6 @@ public class Application.Configuration : Geary.BaseObject {
set { settings.set_boolean(FORMATTING_TOOLBAR_VISIBLE, value); }
}
- public int messages_pane_position {
- get { return settings.get_int(MESSAGES_PANE_POSITION_KEY); }
- set { settings.set_int(MESSAGES_PANE_POSITION_KEY, value); }
- }
-
public bool autoselect {
get { return settings.get_boolean(AUTOSELECT_KEY); }
}
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 47749019..48f8bae6 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1130,15 +1130,10 @@ public class Application.MainWindow :
// This code both loads AND saves the pane positions with live updating. This is more
// resilient against crashes because the value in dconf changes *immediately*, and
// stays saved in the event of a crash.
- config.bind(Configuration.MESSAGES_PANE_POSITION_KEY, this.conversations_paned, "position");
config.bind(Configuration.WINDOW_WIDTH_KEY, this, "window-width");
config.bind(Configuration.WINDOW_HEIGHT_KEY, this, "window-height");
config.bind(Configuration.WINDOW_MAXIMIZE_KEY, this, "window-maximized");
// Update to layout
- if (config.folder_list_pane_position_horizontal == -1) {
- config.folder_list_pane_position_horizontal = config.folder_list_pane_position_old;
- config.messages_pane_position += config.folder_list_pane_position_old;
- }
config.settings.changed[
Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY
].connect(on_change_orientation);
@@ -1655,23 +1650,11 @@ public class Application.MainWindow :
this.folder_paned.orientation = horizontal ? Gtk.Orientation.HORIZONTAL :
Gtk.Orientation.VERTICAL;
- int folder_list_width =
- this.application.config.folder_list_pane_position_horizontal;
if (horizontal) {
- if (!initial)
- this.conversations_paned.position += folder_list_width;
this.folder_box.pack_start(status_bar, false, false);
} else {
- if (!initial)
- this.conversations_paned.position -= folder_list_width;
this.conversation_list_box.pack_start(status_bar, false, false);
}
-
- this.application.config.bind(
- horizontal
- ? Configuration.FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY
- : Configuration.FOLDER_LIST_PANE_POSITION_VERTICAL_KEY,
- this.folder_paned, "position");
}
private void update_headerbar() {
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 31f87df6..aa263253 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -59,15 +59,6 @@ public class MainToolbar : Gtk.Box {
public MainToolbar(Application.Configuration config) {
- // Sync headerbar width with left pane
- config.bind(
- Application.Configuration.MESSAGES_PANE_POSITION_KEY,
- this,
- "left-pane-width",
- SettingsBindFlags.GET
- );
- this.bind_property("left-pane-width", this.folder_header, "width-request", BindingFlags.SYNC_CREATE);
-
if (config.desktop_environment != UNITY) {
this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE);
this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE);
--
2.29.2

View File

@ -0,0 +1,150 @@
From fb311e1c822fb41782c7ff07525c77732a52a971 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 16 Jan 2020 13:17:58 +0100
Subject: [PATCH 046/124] Drop the 2-panes mode
This won't be needed to save horizontal space when using HdyLeaflet, and
it would make porting to it harder.
---
desktop/org.gnome.Geary.gschema.xml | 6 -----
.../application-configuration.vala | 5 ----
.../application/application-main-window.vala | 27 ++-----------------
.../components-preferences-window.vala | 16 -----------
4 files changed, 2 insertions(+), 52 deletions(-)
diff --git a/desktop/org.gnome.Geary.gschema.xml b/desktop/org.gnome.Geary.gschema.xml
index 9850dffd..89354dc2 100644
--- a/desktop/org.gnome.Geary.gschema.xml
+++ b/desktop/org.gnome.Geary.gschema.xml
@@ -21,12 +21,6 @@
<description>The last recorded height of the application window.</description>
</key>
- <key name="folder-list-pane-horizontal" type="b">
- <default>true</default>
- <summary>Orientation of the folder list pane</summary>
- <description>True if the folder list Paned is in the horizontal orientation.</description>
- </key>
-
<key name="formatting-toolbar-visible" type="b">
<default>false</default>
<summary>Show/hide formatting toolbar</summary>
diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala
index 51a11bbf..48542df6 100644
--- a/src/client/application/application-configuration.vala
+++ b/src/client/application/application-configuration.vala
@@ -19,7 +19,6 @@ public class Application.Configuration : Geary.BaseObject {
public const string COMPOSE_AS_HTML_KEY = "compose-as-html";
public const string CONVERSATION_VIEWER_ZOOM_KEY = "conversation-viewer-zoom";
public const string DISPLAY_PREVIEW_KEY = "display-preview";
- public const string FOLDER_LIST_PANE_HORIZONTAL_KEY = "folder-list-pane-horizontal";
public const string FORMATTING_TOOLBAR_VISIBLE = "formatting-toolbar-visible";
public const string OPTIONAL_PLUGINS = "optional-plugins";
public const string SEARCH_STRATEGY_KEY = "search-strategy";
@@ -86,10 +85,6 @@ public class Application.Configuration : Geary.BaseObject {
get { return settings.get_boolean(WINDOW_MAXIMIZE_KEY); }
}
- public bool folder_list_pane_horizontal {
- get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); }
- }
-
public bool formatting_toolbar_visible {
get { return settings.get_boolean(FORMATTING_TOOLBAR_VISIBLE); }
set { settings.set_boolean(FORMATTING_TOOLBAR_VISIBLE, value); }
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 48f8bae6..0bec6614 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -511,7 +511,8 @@ public class Application.MainWindow :
});
setup_layout(application.config);
- on_change_orientation();
+ this.folder_paned.orientation = Gtk.Orientation.HORIZONTAL;
+ this.folder_box.pack_start(status_bar, false, false);
update_command_actions();
update_conversation_actions(NONE);
@@ -1133,10 +1134,6 @@ public class Application.MainWindow :
config.bind(Configuration.WINDOW_WIDTH_KEY, this, "window-width");
config.bind(Configuration.WINDOW_HEIGHT_KEY, this, "window-height");
config.bind(Configuration.WINDOW_MAXIMIZE_KEY, this, "window-maximized");
- // Update to layout
- config.settings.changed[
- Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY
- ].connect(on_change_orientation);
}
private void restore_saved_window_state() {
@@ -1637,26 +1634,6 @@ public class Application.MainWindow :
}
}
- private void on_change_orientation() {
- bool horizontal = this.application.config.folder_list_pane_horizontal;
- bool initial = true;
-
- if (this.status_bar.parent != null) {
- this.status_bar.parent.remove(status_bar);
- initial = false;
- }
-
- GLib.Settings.unbind(this.folder_paned, "position");
- this.folder_paned.orientation = horizontal ? Gtk.Orientation.HORIZONTAL :
- Gtk.Orientation.VERTICAL;
-
- if (horizontal) {
- this.folder_box.pack_start(status_bar, false, false);
- } else {
- this.conversation_list_box.pack_start(status_bar, false, false);
- }
- }
-
private void update_headerbar() {
update_title();
if (this.selected_folder != null) {
diff --git a/src/client/components/components-preferences-window.vala b/src/client/components/components-preferences-window.vala
index b1bdfd7b..ea978a3a 100644
--- a/src/client/components/components-preferences-window.vala
+++ b/src/client/components/components-preferences-window.vala
@@ -136,16 +136,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
display_preview_row.activatable_widget = display_preview;
display_preview_row.add(display_preview);
- var three_pane_view = new Gtk.Switch();
- three_pane_view.valign = CENTER;
-
- var three_pane_view_row = new Hdy.ActionRow();
- /// Translators: Preferences label
- three_pane_view_row.title = _("Use _three pane view");
- three_pane_view_row.use_underline = true;
- three_pane_view_row.activatable_widget = three_pane_view;
- three_pane_view_row.add(three_pane_view);
-
var single_key_shortucts = new Gtk.Switch();
single_key_shortucts.valign = CENTER;
@@ -180,7 +170,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
//group.description = _("General application preferences");
group.add(autoselect_row);
group.add(display_preview_row);
- group.add(three_pane_view_row);
group.add(single_key_shortucts_row);
group.add(startup_notifications_row);
@@ -210,11 +199,6 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
display_preview,
"state"
);
- config.bind(
- Application.Configuration.FOLDER_LIST_PANE_HORIZONTAL_KEY,
- three_pane_view,
- "state"
- );
config.bind(
Application.Configuration.SINGLE_KEY_SHORTCUTS,
single_key_shortucts,
--
2.29.2

View File

@ -0,0 +1,186 @@
From 850efb72380b3e2adc9f29af45a16bc1bd6e884b Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Fri, 17 Jan 2020 18:51:39 +0100
Subject: [PATCH 047/124] application-main-window: Move the conversations
searchbar
Move it at above the conversations but not above the folders. This is
needed to properly split the 3 panes.
---
.../application/application-main-window.vala | 4 +-
ui/application-main-window.ui | 105 +++++++-----------
2 files changed, 44 insertions(+), 65 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 0bec6614..43289e33 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -317,8 +317,6 @@ public class Application.MainWindow :
[GtkChild]
private Gtk.Box main_layout;
[GtkChild]
- private Gtk.Box search_bar_box;
- [GtkChild]
private Gtk.Paned folder_paned;
[GtkChild]
private Gtk.Paned conversations_paned;
@@ -1210,7 +1208,7 @@ public class Application.MainWindow :
// Search bar
this.search_bar = new SearchBar(this.application.engine);
this.search_bar.search_text_changed.connect(on_search);
- this.search_bar_box.pack_start(this.search_bar, false, false, 0);
+ this.conversation_list_box.pack_start(this.search_bar, false, false, 0);
// Folder list
this.folder_list.folder_selected.connect(on_folder_selected);
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index f429a5ee..4a948294 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -28,100 +28,81 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
- <object class="GtkBox" id="search_bar_box">
+ <object class="GtkPaned" id="folder_paned">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
+ <property name="can_focus">True</property>
<child>
- <object class="GtkPaned" id="folder_paned">
+ <object class="GtkBox" id="folder_box">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkBox" id="folder_box">
+ <object class="GtkFrame" id="folder_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="orientation">vertical</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
<child>
- <object class="GtkFrame" id="folder_frame">
+ <object class="GtkScrolledWindow" id="folder_list_scrolled">
+ <property name="width_request">100</property>
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkScrolledWindow" id="folder_list_scrolled">
- <property name="width_request">100</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- </object>
- </child>
- <style>
- <class name="geary-folder-frame"/>
- </style>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
</object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
</child>
+ <style>
+ <class name="geary-folder-frame"/>
+ </style>
</object>
<packing>
- <property name="resize">False</property>
- <property name="shrink">False</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
</packing>
</child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="conversation_list_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkBox" id="conversation_list_box">
+ <object class="GtkFrame" id="conversation_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="orientation">vertical</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
<child>
- <object class="GtkFrame">
+ <object class="GtkScrolledWindow" id="conversation_list_scrolled">
+ <property name="width_request">250</property>
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkScrolledWindow" id="conversation_list_scrolled">
- <property name="width_request">250</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- </child>
- <style>
- <class name="geary-conversation-frame"/>
- </style>
+ <property name="can_focus">True</property>
</object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
</child>
<style>
- <class name="geary-conversation-list-box"/>
+ <class name="geary-conversation-frame"/>
</style>
</object>
<packing>
- <property name="resize">True</property>
- <property name="shrink">False</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
</packing>
</child>
- <style>
- <class name="geary-sidebar-pane-separator"/>
- </style>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
</packing>
</child>
<style>
- <class name="sidebar"/>
+ <class name="geary-sidebar-pane-separator"/>
</style>
</object>
<packing>
--
2.29.2

View File

@ -0,0 +1,206 @@
From 94ab7e5ac6b282e07edf8966da5c69e4fdba0cc8 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Sat, 18 Jan 2020 09:16:32 +0100
Subject: [PATCH 048/124] main-toolbar: Split the folder header
Split it into a folder header and a conversations header. This is needed
to properly split the 3 panes.
Fixes https://gitlab.gnome.org/GNOME/geary/issues/442.
---
src/client/components/main-toolbar.vala | 12 ++-
ui/main-toolbar.ui | 108 +++++++++++++++---------
2 files changed, 77 insertions(+), 43 deletions(-)
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index aa263253..4ee02079 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -28,10 +28,14 @@ public class MainToolbar : Gtk.Box {
[GtkChild]
private Gtk.HeaderBar folder_header;
[GtkChild]
- private Gtk.ToggleButton search_conversations_button;
- [GtkChild]
private Gtk.MenuButton main_menu_button;
+ // Conversations header elements
+ [GtkChild]
+ private Gtk.HeaderBar conversations_header;
+ [GtkChild]
+ private Gtk.ToggleButton search_conversations_button;
+
// Conversation header elements
[GtkChild]
private Gtk.HeaderBar conversation_header;
@@ -60,8 +64,8 @@ public class MainToolbar : Gtk.Box {
public MainToolbar(Application.Configuration config) {
if (config.desktop_environment != UNITY) {
- this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE);
- this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE);
+ this.bind_property("account", this.conversations_header, "title", BindingFlags.SYNC_CREATE);
+ this.bind_property("folder", this.conversations_header, "subtitle", BindingFlags.SYNC_CREATE);
}
// Assemble the main/mark menus
diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui
index 874f5b4b..732a01b9 100644
--- a/ui/main-toolbar.ui
+++ b/ui/main-toolbar.ui
@@ -11,68 +11,97 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkHeaderBar" id="folder_header">
+ <object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
<child>
- <object class="GtkButton" id="compose_new_message_button">
+ <object class="GtkHeaderBar" id="folder_header">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="focus_on_click">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes" context="tooltip">Compose Message</property>
- <property name="action_name">app.compose</property>
- <property name="always_show_image">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
+ <property name="title">Mail</property>
<child>
- <object class="GtkImage" id="compose_new_message_image">
+ <object class="GtkMenuButton" id="main_menu_button">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">text-editor-symbolic</property>
+ <property name="can_focus">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="receives_default">False</property>
+ <property name="always_show_image">True</property>
+ <child>
+ <object class="GtkImage" id="main_menu_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">open-menu-symbolic</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
</child>
</object>
</child>
<child>
- <object class="GtkMenuButton" id="main_menu_button">
+ <object class="GtkSeparator" id="folder_separator">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="focus_on_click">False</property>
- <property name="receives_default">False</property>
- <property name="always_show_image">True</property>
- <child>
- <object class="GtkImage" id="main_menu_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">open-menu-symbolic</property>
- </object>
- </child>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <style>
+ <class name="sidebar"/>
+ </style>
</object>
<packing>
- <property name="pack_type">end</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="search_conversations_button">
+ <object class="GtkHeaderBar" id="conversations_header">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="focus_on_click">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Toggle search bar</property>
- <property name="always_show_image">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
<child>
- <object class="GtkImage" id="search_conversations_image">
+ <object class="GtkButton" id="compose_new_message_button">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">preferences-system-search-symbolic</property>
+ <property name="can_focus">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes" context="tooltip">Compose Message</property>
+ <property name="action_name">app.compose</property>
+ <property name="always_show_image">True</property>
+ <child>
+ <object class="GtkImage" id="compose_new_message_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">text-editor-symbolic</property>
+ </object>
+ </child>
</object>
</child>
+ <child>
+ <object class="GtkToggleButton" id="search_conversations_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Toggle search bar</property>
+ <property name="always_show_image">True</property>
+ <child>
+ <object class="GtkImage" id="search_conversations_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">preferences-system-search-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
</object>
- <packing>
- <property name="pack_type">end</property>
- <property name="position">3</property>
- </packing>
</child>
</object>
<packing>
@@ -82,7 +111,7 @@
</packing>
</child>
<child>
- <object class="GtkSeparator" id="header_separator">
+ <object class="GtkSeparator" id="conversations_separator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
@@ -342,6 +371,7 @@
<object class="HdyHeaderGroup" id="header_group">
<headerbars>
<headerbar name="folder_header"/>
+ <headerbar name="conversations_header"/>
<headerbar name="conversation_header"/>
</headerbars>
</object>
--
2.29.2

View File

@ -0,0 +1,55 @@
From 7d4a61d431e072dbcacda2d089b587fe52257da6 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 16 Jan 2020 13:50:03 +0100
Subject: [PATCH 049/124] main-toolbar: Add add_to_size_groups()
This will be used to sync requests of the panes.
---
src/client/components/main-toolbar.vala | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 4ee02079..6ea472a8 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -30,12 +30,18 @@ public class MainToolbar : Gtk.Box {
[GtkChild]
private Gtk.MenuButton main_menu_button;
+ [GtkChild]
+ private Gtk.Separator folder_separator;
+
// Conversations header elements
[GtkChild]
private Gtk.HeaderBar conversations_header;
[GtkChild]
private Gtk.ToggleButton search_conversations_button;
+ [GtkChild]
+ private Gtk.Separator conversations_separator;
+
// Conversation header elements
[GtkChild]
private Gtk.HeaderBar conversation_header;
@@ -105,6 +111,18 @@ public class MainToolbar : Gtk.Box {
update_conversation_buttons();
}
+ public void add_to_size_groups(Gtk.SizeGroup folder_group,
+ Gtk.SizeGroup folder_separator_group,
+ Gtk.SizeGroup conversations_group,
+ Gtk.SizeGroup conversations_separator_group,
+ Gtk.SizeGroup conversation_group) {
+ folder_group.add_widget(folder_header);
+ folder_separator_group.add_widget(folder_separator);
+ conversations_group.add_widget(conversations_header);
+ conversations_separator_group.add_widget(conversations_separator);
+ conversation_group.add_widget(conversation_header);
+ }
+
// Updates tooltip text depending on number of conversations selected.
private void update_conversation_buttons() {
this.mark_message_button.tooltip_text = ngettext(
--
2.29.2

View File

@ -0,0 +1,82 @@
From edfb1a2cf371ab743cbf804cbc4d34b7e4cfb8c6 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 16 Jan 2020 13:52:03 +0100
Subject: [PATCH 050/124] application-main-window: Sync the pane size request
Bind each pane's elements minimum size requests via size groups.
---
.../application/application-main-window.vala | 17 +++++++++++++++
ui/application-main-window.ui | 21 +++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 43289e33..4c6d1038 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -328,6 +328,17 @@ public class Application.MainWindow :
private Gtk.Box conversation_list_box;
[GtkChild]
private Gtk.ScrolledWindow conversation_list_scrolled;
+ [GtkChild]
+ private Gtk.SizeGroup folder_size_group;
+ [GtkChild]
+ private Gtk.SizeGroup folder_separator_size_group;
+ [GtkChild]
+ private Gtk.SizeGroup conversations_size_group;
+ [GtkChild]
+ private Gtk.SizeGroup conversations_separator_size_group;
+ [GtkChild]
+ private Gtk.SizeGroup conversation_size_group;
+
[GtkChild]
private Gtk.Overlay overlay;
@@ -1239,9 +1250,15 @@ public class Application.MainWindow :
);
this.conversations_paned.pack2(this.conversation_viewer, true, false);
+ this.conversation_size_group.add_widget(this.conversation_viewer);
// Main toolbar
this.main_toolbar = new MainToolbar(config);
+ this.main_toolbar.add_to_size_groups(this.folder_size_group,
+ this.folder_separator_size_group,
+ this.conversations_size_group,
+ this.conversations_separator_size_group,
+ this.conversation_size_group);
this.main_toolbar.move_folder_menu.folder_selected.connect(on_move_conversation);
this.main_toolbar.copy_folder_menu.folder_selected.connect(on_copy_conversation);
this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled",
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index 4a948294..872f31bd 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -138,4 +138,25 @@
<class name="geary-main-window"/>
</style>
</template>
+ <object class="GtkSizeGroup" id="folder_size_group">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="folder_box"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="folder_separator_size_group">
+ <property name="mode">horizontal</property>
+ </object>
+ <object class="GtkSizeGroup" id="conversations_size_group">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="conversation_list_box"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="conversations_separator_size_group">
+ <property name="mode">horizontal</property>
+ </object>
+ <object class="GtkSizeGroup" id="conversation_size_group">
+ <property name="mode">horizontal</property>
+ </object>
</interface>
--
2.29.2

View File

@ -0,0 +1,338 @@
From 12a7101ba55bb646161e9997485036744121f542 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Thu, 24 Oct 2019 14:36:04 +0200
Subject: [PATCH 051/124] Use leaflets in the UI
There is no way to navigate into the app, but it's a start.
v2: replace expand with child vexpand
---
.../application/application-main-window.vala | 8 +--
src/client/components/main-toolbar.vala | 5 +-
.../conversation-viewer.vala | 4 ++
ui/application-main-window.ui | 52 ++++++++++++++-----
ui/geary.css | 11 ++++
ui/main-toolbar.ui | 33 +++++++-----
6 files changed, 81 insertions(+), 32 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 4c6d1038..b56079cf 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -317,9 +317,9 @@ public class Application.MainWindow :
[GtkChild]
private Gtk.Box main_layout;
[GtkChild]
- private Gtk.Paned folder_paned;
+ private Hdy.Leaflet main_leaflet;
[GtkChild]
- private Gtk.Paned conversations_paned;
+ private Hdy.Leaflet conversations_leaflet;
[GtkChild]
private Gtk.Box folder_box;
[GtkChild]
@@ -520,7 +520,6 @@ public class Application.MainWindow :
});
setup_layout(application.config);
- this.folder_paned.orientation = Gtk.Orientation.HORIZONTAL;
this.folder_box.pack_start(status_bar, false, false);
update_command_actions();
@@ -1249,8 +1248,9 @@ public class Application.MainWindow :
on_conversation_view_added
);
- this.conversations_paned.pack2(this.conversation_viewer, true, false);
+ this.conversation_viewer.hexpand = true;
this.conversation_size_group.add_widget(this.conversation_viewer);
+ this.main_leaflet.add_with_properties(this.conversation_viewer, "name", "conversation", null);
// Main toolbar
this.main_toolbar = new MainToolbar(config);
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 6ea472a8..286546d5 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -6,7 +6,7 @@
// Draws the main toolbar.
[GtkTemplate (ui = "/org/gnome/Geary/main-toolbar.ui")]
-public class MainToolbar : Gtk.Box {
+public class MainToolbar : Hdy.Leaflet {
// How wide the left pane should be. Auto-synced with our settings
public int left_pane_width { get; set; }
// Used to form the title of the folder header
@@ -97,7 +97,8 @@ public class MainToolbar : Gtk.Box {
public void set_conversation_header(Gtk.HeaderBar header) {
conversation_header.hide();
this.header_group.add_gtk_header_bar(header);
- pack_start(header, true, true);
+ header.hexpand = true;
+ add(header);
}
public void remove_conversation_header(Gtk.HeaderBar header) {
diff --git a/src/client/conversation-viewer/conversation-viewer.vala b/src/client/conversation-viewer/conversation-viewer.vala
index f04a1d26..eec5f6a4 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -68,6 +68,10 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
public signal void conversation_removed(ConversationListBox list);
+ static construct {
+ set_css_name("geary-conversation-viewer");
+ }
+
/**
* Constructs a new conversation view instance.
*/
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index 872f31bd..f7bb884e 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -24,13 +24,18 @@
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkPaned" id="conversations_paned">
+ <object class="HdyLeaflet" id="main_leaflet">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="can_swipe_back">True</property>
+ <property name="transition_type">over</property>
<child>
- <object class="GtkPaned" id="folder_paned">
+ <object class="HdyLeaflet" id="conversations_leaflet">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="hexpand_set">True</property>
+ <property name="can_swipe_back">True</property>
+ <property name="transition_type">over</property>
<child>
<object class="GtkBox" id="folder_box">
<property name="visible">True</property>
@@ -40,6 +45,7 @@
<object class="GtkFrame" id="folder_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="vexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
@@ -55,15 +61,26 @@
</style>
</object>
<packing>
- <property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
- <property name="resize">False</property>
- <property name="shrink">False</property>
+ <property name="name">folder</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="folder_separator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <style>
+ <class name="sidebar"/>
+ </style>
+ </object>
+ <packing>
+ <property name="navigatable">False</property>
</packing>
</child>
<child>
@@ -97,22 +114,27 @@
</child>
</object>
<packing>
- <property name="resize">True</property>
- <property name="shrink">False</property>
+ <property name="name">conversations</property>
</packing>
</child>
+ </object>
+ <packing>
+ <property name="name">conversations</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="conversations_separator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<style>
<class name="geary-sidebar-pane-separator"/>
</style>
</object>
<packing>
- <property name="resize">False</property>
- <property name="shrink">False</property>
+ <property name="navigatable">False</property>
</packing>
</child>
- <child>
- <placeholder/>
- </child>
</object>
<packing>
<property name="expand">True</property>
@@ -146,6 +168,9 @@
</object>
<object class="GtkSizeGroup" id="folder_separator_size_group">
<property name="mode">horizontal</property>
+ <widgets>
+ <widget name="folder_separator"/>
+ </widgets>
</object>
<object class="GtkSizeGroup" id="conversations_size_group">
<property name="mode">horizontal</property>
@@ -155,6 +180,9 @@
</object>
<object class="GtkSizeGroup" id="conversations_separator_size_group">
<property name="mode">horizontal</property>
+ <widgets>
+ <widget name="conversations_separator"/>
+ </widgets>
</object>
<object class="GtkSizeGroup" id="conversation_size_group">
<property name="mode">horizontal</property>
diff --git a/ui/geary.css b/ui/geary.css
index 0ddfab30..2d1d48c3 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -12,12 +12,23 @@
border-left-width: 0;
border-top-width: 0;
border-right-width: 0;
+ min-width: 300px;
}
.geary-conversation-frame > border {
border-left-width: 0;
border-top-width: 0;
border-right-width: 0;
+ min-width: 360px;
}
+
+treeview.sidebar {
+ border: none;
+}
+
+geary-conversation-viewer {
+ min-width: 360px;
+}
+
/* For 3-pane mode only */
.geary-sidebar-pane-separator.vertical .conversation-frame > border {
border-bottom-width: 0;
diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui
index 732a01b9..441cd146 100644
--- a/ui/main-toolbar.ui
+++ b/ui/main-toolbar.ui
@@ -7,13 +7,18 @@
<property name="can_focus">False</property>
<property name="icon_name">mail-archive-symbolic</property>
</object>
- <template class="MainToolbar" parent="GtkBox">
+ <template class="MainToolbar" parent="HdyLeaflet">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="can_swipe_back">True</property>
+ <property name="transition_type">over</property>
<child>
- <object class="GtkBox">
+ <object class="HdyLeaflet" id="conversations_leaflet">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="can_swipe_back">True</property>
+ <property name="transition_type">over</property>
+ <property name="hexpand_set">True</property>
<child>
<object class="GtkHeaderBar" id="folder_header">
<property name="visible">True</property>
@@ -41,6 +46,9 @@
</packing>
</child>
</object>
+ <packing>
+ <property name="name">folder</property>
+ </packing>
</child>
<child>
<object class="GtkSeparator" id="folder_separator">
@@ -52,15 +60,14 @@
</style>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
+ <property name="navigatable">False</property>
</packing>
</child>
<child>
<object class="GtkHeaderBar" id="conversations_header">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="hexpand">True</property>
<property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
<child>
<object class="GtkButton" id="compose_new_message_button">
@@ -102,12 +109,13 @@
</packing>
</child>
</object>
+ <packing>
+ <property name="name">conversations</property>
+ </packing>
</child>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
+ <property name="name">conversations</property>
</packing>
</child>
<child>
@@ -120,15 +128,14 @@
</style>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
+ <property name="navigatable">False</property>
</packing>
</child>
<child>
<object class="GtkHeaderBar" id="conversation_header">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="hexpand">True</property>
<property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
<child>
<object class="GtkBox" id="reply_forward_buttons">
@@ -362,9 +369,7 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
+ <property name="name">conversation</property>
</packing>
</child>
</template>
--
2.29.2

View File

@ -0,0 +1,40 @@
From 2139636d82b67414dd22170894bb61d2e9d1b415 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Sat, 18 Jan 2020 12:58:18 +0100
Subject: [PATCH 052/124] main-toolbar: Add add_to_swipe_groups()
This will be used to sync the swipe state of the leaflets.
---
src/client/components/main-toolbar.vala | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 286546d5..c7bdab8f 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -24,6 +24,9 @@ public class MainToolbar : Hdy.Leaflet {
public int selected_conversations { get; set; }
+ [GtkChild]
+ private Hdy.Leaflet conversations_leaflet;
+
// Folder header elements
[GtkChild]
private Gtk.HeaderBar folder_header;
@@ -124,6 +127,12 @@ public class MainToolbar : Hdy.Leaflet {
conversation_group.add_widget(conversation_header);
}
+ public void add_to_swipe_groups(Hdy.SwipeGroup conversations_group,
+ Hdy.SwipeGroup conversation_group) {
+ conversations_group.add_swipeable(this.conversations_leaflet);
+ conversation_group.add_swipeable(this);
+ }
+
// Updates tooltip text depending on number of conversations selected.
private void update_conversation_buttons() {
this.mark_message_button.tooltip_text = ngettext(
--
2.29.2

View File

@ -0,0 +1,58 @@
From 28e319267446fe96c070b85d6a20096f787fcdcc Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Sat, 18 Jan 2020 13:04:10 +0100
Subject: [PATCH 053/124] application-main-window: Sync the leaflets' swipe
state
This keeps the swiping state of the leaflets in sync.
---
src/client/application/application-main-window.vala | 6 ++++++
ui/application-main-window.ui | 10 ++++++++++
2 files changed, 16 insertions(+)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index b56079cf..3af1b6aa 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -338,6 +338,10 @@ public class Application.MainWindow :
private Gtk.SizeGroup conversations_separator_size_group;
[GtkChild]
private Gtk.SizeGroup conversation_size_group;
+ [GtkChild]
+ private Hdy.SwipeGroup conversations_swipe_group;
+ [GtkChild]
+ private Hdy.SwipeGroup conversation_swipe_group;
[GtkChild]
private Gtk.Overlay overlay;
@@ -1259,6 +1263,8 @@ public class Application.MainWindow :
this.conversations_size_group,
this.conversations_separator_size_group,
this.conversation_size_group);
+ this.main_toolbar.add_to_swipe_groups(this.conversations_swipe_group,
+ this.conversation_swipe_group);
this.main_toolbar.move_folder_menu.folder_selected.connect(on_move_conversation);
this.main_toolbar.copy_folder_menu.folder_selected.connect(on_copy_conversation);
this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled",
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index f7bb884e..fe66491b 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -187,4 +187,14 @@
<object class="GtkSizeGroup" id="conversation_size_group">
<property name="mode">horizontal</property>
</object>
+ <object class="HdySwipeGroup" id="conversations_swipe_group">
+ <swipeables>
+ <swipeable name="conversations_leaflet"/>
+ </swipeables>
+ </object>
+ <object class="HdySwipeGroup" id="conversation_swipe_group">
+ <swipeables>
+ <swipeable name="main_leaflet"/>
+ </swipeables>
+ </object>
</interface>
--
2.29.2

View File

@ -0,0 +1,66 @@
From 115b055e7b3dd267c44efacaf3fc7272a81d9d96 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Mon, 5 Oct 2020 17:20:14 +0200
Subject: [PATCH 054/124] main-window: Add leaflet navigation with Alt+Arrow
keys
---
.../application/application-main-window.vala | 35 +++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 3af1b6aa..bb5a9404 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1797,7 +1797,20 @@ public class Application.MainWindow :
private void focus_next_pane() {
var focus = get_focus();
- if (focus != null) {
+
+ if (main_leaflet.folded) {
+ if (main_leaflet.visible_child_name == "conversations") {
+ if (conversations_leaflet.folded &&
+ conversations_leaflet.visible_child_name == "folder" ||
+ focus == this.folder_list) {
+ conversations_leaflet.navigate(Hdy.NavigationDirection.FORWARD);
+ focus = this.conversation_list_view;
+ } else {
+ main_leaflet.navigate(Hdy.NavigationDirection.FORWARD);
+ focus = this.conversation_viewer.visible_child;
+ }
+ }
+ } else if (focus != null) {
if (focus == this.folder_list ||
focus.is_ancestor(this.folder_list)) {
focus = this.conversation_list_view;
@@ -1819,7 +1832,25 @@ public class Application.MainWindow :
private void focus_previous_pane() {
var focus = get_focus();
- if (focus != null) {
+
+ if (main_leaflet.folded) {
+ if (main_leaflet.visible_child_name == "conversations") {
+ if (conversations_leaflet.folded) {
+ if (conversations_leaflet.visible_child_name == "conversations") {
+ conversations_leaflet.navigate(Hdy.NavigationDirection.BACK);
+ focus = this.folder_list;
+ }
+ } else {
+ if (focus == this.conversation_list_view)
+ focus = this.folder_list;
+ else
+ focus = this.conversation_list_view;
+ }
+ } else {
+ main_leaflet.navigate(Hdy.NavigationDirection.BACK);
+ focus = this.conversation_list_view;
+ }
+ } else if (focus != null) {
if (focus == this.folder_list ||
focus.is_ancestor(this.folder_list)) {
focus = this.conversation_viewer.visible_child;
--
2.29.2

View File

@ -0,0 +1,103 @@
From 28a19775b8689f08c4e6c280e9414547d23d42c7 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Wed, 30 Sep 2020 17:38:24 +0200
Subject: [PATCH 055/124] main-toolbar: Add back buttons for leaflet navigation
---
.../application/application-main-window.vala | 2 +
ui/main-toolbar.ui | 52 +++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index bb5a9404..c3cf2d58 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -31,6 +31,7 @@ public class Application.MainWindow :
public const string ACTION_TOGGLE_JUNK = "toggle-conversation-junk";
public const string ACTION_TRASH_CONVERSATION = "trash-conversation";
public const string ACTION_ZOOM = "zoom";
+ public const string ACTION_NAVIGATION_BACK = "navigation-back";
private const ActionEntry[] EDIT_ACTIONS = {
{ Action.Edit.UNDO, on_undo },
@@ -42,6 +43,7 @@ public class Application.MainWindow :
{ ACTION_FIND_IN_CONVERSATION, on_find_in_conversation_action },
{ ACTION_SEARCH, on_search_activated },
+ { ACTION_NAVIGATION_BACK, focus_previous_pane},
// Message actions
{ ACTION_REPLY_CONVERSATION, on_reply_conversation },
{ ACTION_REPLY_ALL_CONVERSATION, on_reply_all_conversation },
diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui
index 441cd146..871aba25 100644
--- a/ui/main-toolbar.ui
+++ b/ui/main-toolbar.ui
@@ -69,6 +69,32 @@
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
+ <child>
+ <object class="GtkButton" id="conversations_back">
+ <property name="can_focus">False</property>
+ <property name="receives_default">False</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <property name="visible" bind-source="conversations_leaflet" bind-property="folded" bind-flags="sync-create"/>
+ <property name="action_name">win.navigation-back</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-conversations-back">
+ <property name="accessible-name" translatable="yes">Back</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="conversations_back_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
<child>
<object class="GtkButton" id="compose_new_message_button">
<property name="visible">True</property>
@@ -137,6 +163,32 @@
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="show_close_button" bind-source="MainToolbar" bind-property="show_close_button" bind-flags="sync-create"/>
+ <child>
+ <object class="GtkButton" id="conversation_back">
+ <property name="can_focus">False</property>
+ <property name="receives_default">False</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <property name="visible" bind-source="MainToolbar" bind-property="folded" bind-flags="sync-create"/>
+ <property name="action_name">win.navigation-back</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-conversation-back">
+ <property name="accessible-name" translatable="yes">Back</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="conversation_back_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
<child>
<object class="GtkBox" id="reply_forward_buttons">
<property name="visible">True</property>
--
2.29.2

View File

@ -0,0 +1,132 @@
From 49823ec05414e11275f313fb0ca5a48fcab7ddda Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 6 Oct 2020 10:41:33 +0200
Subject: [PATCH 056/124] application-main-window: add navigation via signle
click on folder/conversation
Opening a conversation in a new window is currently disabled, because we
need a new key/button combination.
---
src/client/application/application-main-window.vala | 11 +++++++++++
.../conversation-list/conversation-list-view.vala | 1 +
src/client/folder-list/folder-list-tree.vala | 10 ++++++++++
src/client/sidebar/sidebar-tree.vala | 11 +++++++++++
4 files changed, 33 insertions(+)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index c3cf2d58..97960660 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1230,6 +1230,7 @@ public class Application.MainWindow :
this.folder_list.folder_selected.connect(on_folder_selected);
this.folder_list.move_conversation.connect(on_move_conversation);
this.folder_list.copy_conversation.connect(on_copy_conversation);
+ this.folder_list.folder_activated.connect(on_folder_activated);
this.folder_list_scrolled.add(this.folder_list);
// Conversation list
@@ -2135,7 +2136,16 @@ public class Application.MainWindow :
}
}
+ private void on_folder_activated(Geary.Folder? folder) {
+ if (folder != null)
+ focus_next_pane();
+ }
+
private void on_conversation_activated(Geary.App.Conversation activated) {
+ if (main_leaflet.folded) {
+ focus_next_pane();
+ }
+ /* TODO: find correct UX for opening the conversation in a new window
if (this.selected_folder != null) {
if (this.selected_folder.used_as != DRAFTS) {
this.application.new_window.begin(
@@ -2154,6 +2164,7 @@ public class Application.MainWindow :
);
}
}
+ */
}
private void on_find_in_conversation_action() {
diff --git a/src/client/conversation-list/conversation-list-view.vala b/src/client/conversation-list/conversation-list-view.vala
index a4d5cca6..dcced90d 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -42,6 +42,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
base_ref();
set_show_expanders(false);
set_headers_visible(false);
+ set_activate_on_single_click(true);
this.config = config;
diff --git a/src/client/folder-list/folder-list-tree.vala b/src/client/folder-list/folder-list-tree.vala
index fb91347e..f820d12f 100644
--- a/src/client/folder-list/folder-list-tree.vala
+++ b/src/client/folder-list/folder-list-tree.vala
@@ -16,6 +16,7 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
public signal void folder_selected(Geary.Folder? folder);
+ public signal void folder_activated(Geary.Folder? folder);
public signal void copy_conversation(Geary.Folder folder);
public signal void move_conversation(Geary.Folder folder);
@@ -30,7 +31,9 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
public Tree() {
base(TARGET_ENTRY_LIST, Gdk.DragAction.COPY | Gdk.DragAction.MOVE, drop_handler);
base_ref();
+ set_activate_on_single_click(true);
entry_selected.connect(on_entry_selected);
+ entry_activated.connect(on_entry_activated);
// GtkTreeView binds Ctrl+N to "move cursor to next". Not so interested in that, so we'll
// remove it.
@@ -87,6 +90,13 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
}
}
+ private void on_entry_activated(Sidebar.SelectableEntry selectable) {
+ AbstractFolderEntry? entry = selectable as AbstractFolderEntry;
+ if (entry != null) {
+ folder_activated(entry.folder);
+ }
+ }
+
public void set_user_folders_root_name(Geary.Account account, string name) {
if (account_branches.has_key(account))
account_branches.get(account).user_folder_group.rename(name);
diff --git a/src/client/sidebar/sidebar-tree.vala b/src/client/sidebar/sidebar-tree.vala
index a5f95092..78d73e98 100644
--- a/src/client/sidebar/sidebar-tree.vala
+++ b/src/client/sidebar/sidebar-tree.vala
@@ -80,6 +80,8 @@ public class Sidebar.Tree : Gtk.TreeView {
public signal void entry_selected(Sidebar.SelectableEntry selectable);
+ public signal void entry_activated(Sidebar.SelectableEntry selectable);
+
public signal void selected_entry_removed(Sidebar.SelectableEntry removed);
public signal void branch_added(Sidebar.Branch branch);
@@ -298,6 +300,15 @@ public class Sidebar.Tree : Gtk.TreeView {
return true;
}
+ public override void row_activated(Gtk.TreePath path, Gtk.TreeViewColumn column) {
+ EntryWrapper? wrapper = get_wrapper_at_path(path);
+ if (wrapper != null) {
+ Sidebar.SelectableEntry? selectable = wrapper.entry as Sidebar.SelectableEntry;
+ if (selectable != null)
+ entry_activated(selectable);
+ }
+ }
+
public override void cursor_changed() {
Gtk.TreePath? path = get_selected_path();
if (path == null) {
--
2.29.2

View File

@ -0,0 +1,35 @@
From d97d86cbc73dc0dc565494c1083b5052457afe04 Mon Sep 17 00:00:00 2001
From: Adrien Plazas <kekun.plazas@laposte.net>
Date: Wed, 16 Sep 2020 13:19:00 +0200
Subject: [PATCH 057/124] toolbar header group
---
ui/main-toolbar.ui | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui
index 871aba25..5d58af6f 100644
--- a/ui/main-toolbar.ui
+++ b/ui/main-toolbar.ui
@@ -425,10 +425,17 @@
</packing>
</child>
</template>
- <object class="HdyHeaderGroup" id="header_group">
+ <object class="HdyHeaderGroup" id="conversations_header_group">
+ <property name="decorate-all" bind-source="conversations_leaflet" bind-property="folded" bind-flags="sync-create"/>
<headerbars>
<headerbar name="folder_header"/>
<headerbar name="conversations_header"/>
+ </headerbars>
+ </object>
+ <object class="HdyHeaderGroup" id="header_group">
+ <property name="decorate-all" bind-source="MainToolbar" bind-property="folded" bind-flags="sync-create"/>
+ <headerbars>
+ <headerbar name="conversations_header_group"/>
<headerbar name="conversation_header"/>
</headerbars>
</object>
--
2.29.2

View File

@ -0,0 +1,72 @@
From 1f2896e9a7ba02c0a909c91bb551e596d2d72230 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Thu, 1 Oct 2020 10:40:41 +0200
Subject: [PATCH 058/124] composer: Switch leaflet to composer when folded
---
src/client/application/application-main-window.vala | 2 ++
src/client/components/main-toolbar.vala | 13 +++++++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 97960660..c2b0954b 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -916,6 +916,8 @@ public class Application.MainWindow :
} else {
this.conversation_viewer.do_compose(composer);
}
+ // Show the correct leaflet
+ this.main_leaflet.set_visible_child_name("conversation");
}
}
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index c7bdab8f..0ecb599a 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -64,6 +64,8 @@ public class MainToolbar : Hdy.Leaflet {
[GtkChild]
private Hdy.HeaderGroup header_group;
+ Gtk.SizeGroup conversation_group;
+
private bool show_trash_button = true;
// Load these at construction time
@@ -98,16 +100,22 @@ public class MainToolbar : Hdy.Leaflet {
}
public void set_conversation_header(Gtk.HeaderBar header) {
- conversation_header.hide();
+ remove(conversation_header);
this.header_group.add_gtk_header_bar(header);
header.hexpand = true;
+ conversation_group.remove_widget(conversation_header);
+ conversation_group.add_widget(header);
add(header);
+ child_set(header, "name", "conversation", null);
}
public void remove_conversation_header(Gtk.HeaderBar header) {
remove(header);
this.header_group.remove_gtk_header_bar(header);
- conversation_header.show();
+ conversation_group.remove_widget(header);
+ conversation_group.add_widget(conversation_header);
+ add(conversation_header);
+ child_set(conversation_header, "name", "conversation", null);
}
public void update_trash_button(bool show_trash) {
@@ -125,6 +133,7 @@ public class MainToolbar : Hdy.Leaflet {
conversations_group.add_widget(conversations_header);
conversations_separator_group.add_widget(conversations_separator);
conversation_group.add_widget(conversation_header);
+ this.conversation_group = conversation_group;
}
public void add_to_swipe_groups(Hdy.SwipeGroup conversations_group,
--
2.29.2

View File

@ -0,0 +1,163 @@
From 0b743ab0d396cbbaad5aebaebed235ecd0f1a564 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 6 Oct 2020 13:34:30 +0200
Subject: [PATCH 059/124] conversation-list: use shift+activate to open
conversation in new window
Open on shift+double click and shift+space/enter the selected
conversation in a new window.
---
.../application/application-main-window.vala | 14 ++---
.../conversation-list-view.vala | 61 ++++++++++++++++---
2 files changed, 58 insertions(+), 17 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index c2b0954b..01b7b9c6 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -2143,17 +2143,16 @@ public class Application.MainWindow :
focus_next_pane();
}
- private void on_conversation_activated(Geary.App.Conversation activated) {
- if (main_leaflet.folded) {
- focus_next_pane();
- }
- /* TODO: find correct UX for opening the conversation in a new window
- if (this.selected_folder != null) {
+ private void on_conversation_activated(Geary.App.Conversation activated, bool single) {
+ if (single) {
+ if (main_leaflet.folded)
+ focus_next_pane();
+ } else if (this.selected_folder != null) {
if (this.selected_folder.used_as != DRAFTS) {
this.application.new_window.begin(
this.selected_folder,
this.conversation_list_view.copy_selected()
- );
+ );
} else {
// TODO: Determine how to map between conversations
// and drafts correctly.
@@ -2166,7 +2165,6 @@ public class Application.MainWindow :
);
}
}
- */
}
private void on_find_in_conversation_action() {
diff --git a/src/client/conversation-list/conversation-list-view.vala b/src/client/conversation-list/conversation-list-view.vala
index dcced90d..0baa66b4 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -17,6 +17,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null;
private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
private Geary.IdleManager selection_update;
+ private Gtk.GestureMultiPress gesture;
// Determines if the next folder scan should avoid selecting a
// conversation when autoselect is enabled
@@ -26,7 +27,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
// Signal for when a conversation has been double-clicked, or selected and enter is pressed.
- public signal void conversation_activated(Geary.App.Conversation activated);
+ public signal void conversation_activated(Geary.App.Conversation activated, bool single = false);
public virtual signal void load_more() {
enable_load_more = false;
@@ -42,7 +43,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
base_ref();
set_show_expanders(false);
set_headers_visible(false);
- set_activate_on_single_click(true);
this.config = config;
@@ -53,11 +53,13 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
Gtk.TreeSelection selection = get_selection();
selection.set_mode(Gtk.SelectionMode.MULTIPLE);
style_updated.connect(on_style_changed);
- row_activated.connect(on_row_activated);
notify["vadjustment"].connect(on_vadjustment_changed);
+ key_press_event.connect(on_key_press);
button_press_event.connect(on_button_press);
+ gesture = new Gtk.GestureMultiPress(this);
+ gesture.pressed.connect(on_gesture_pressed);
// Set up drag and drop.
Gtk.drag_source_set(this, Gdk.ModifierType.BUTTON1_MASK, FolderList.Tree.TARGET_ENTRY_LIST,
@@ -270,6 +272,53 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
return parent.get_vadjustment();
}
+ private void on_gesture_pressed(int n_press, double x, double y) {
+ if (gesture.get_current_button() != Gdk.BUTTON_PRIMARY)
+ return;
+
+ Gtk.TreePath? path;
+ get_path_at_pos((int) x, (int) y, out path, null, null, null);
+
+ // If the user clicked in an empty area, do nothing.
+ if (path == null)
+ return;
+
+ Geary.App.Conversation? c = get_model().get_conversation_at_path(path);
+ if (c == null)
+ return;
+
+ Gdk.Event event = gesture.get_last_event(gesture.get_current_sequence());
+ Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask();
+
+ Gdk.ModifierType state_mask;
+ event.get_state(out state_mask);
+
+ if ((state_mask & modifiers) == 0 && n_press == 1) {
+ conversation_activated(c, true);
+ } else if ((state_mask & modifiers) == Gdk.ModifierType.SHIFT_MASK && n_press == 2) {
+ conversation_activated(c);
+ }
+ }
+
+ private bool on_key_press(Gdk.EventKey event) {
+ if (this.selected.size != 1)
+ return false;
+
+ Geary.App.Conversation? c = this.selected.to_array()[0];
+ if (c == null)
+ return false;
+
+ Gdk.ModifierType modifiers = Gtk.accelerator_get_default_mod_mask();
+
+ if (event.keyval == Gdk.Key.Return ||
+ event.keyval == Gdk.Key.ISO_Enter ||
+ event.keyval == Gdk.Key.KP_Enter ||
+ event.keyval == Gdk.Key.space ||
+ event.keyval == Gdk.Key.KP_Space)
+ conversation_activated(c, !((event.state & modifiers) == Gdk.ModifierType.SHIFT_MASK));
+ return false;
+ }
+
private bool on_button_press(Gdk.EventButton event) {
// Get the coordinates on the cell as well as the clicked path.
int cell_x;
@@ -576,12 +625,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
return false;
}
- private void on_row_activated(Gtk.TreePath path) {
- Geary.App.Conversation? c = get_model().get_conversation_at_path(path);
- if (c != null)
- conversation_activated(c);
- }
-
// Enable/disable hover effect on all selected cells.
private void set_hover_selected(bool hover) {
ConversationListCellRenderer.set_hover_selected(hover);
--
2.29.2

View File

@ -0,0 +1,65 @@
From d6c546e2d555d20e1dd259117822b4d4c8b7152c Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Tue, 6 Oct 2020 17:31:49 +0200
Subject: [PATCH 060/124] composer: close the composer when navigating back
---
src/client/application/application-main-window.vala | 11 +++++++++++
src/client/composer/composer-widget.vala | 5 +++++
ui/application-main-window.ui | 2 ++
3 files changed, 18 insertions(+)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 01b7b9c6..e1e55d0e 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1955,6 +1955,17 @@ public class Application.MainWindow :
return Gdk.EVENT_STOP;
}
+ [GtkCallback]
+ private void on_main_leaflet_visible_child_changed() {
+ if (main_leaflet.child_transition_running)
+ return;
+
+ if (main_leaflet.visible_child_name == "conversations" && main_leaflet.folded)
+ if (this.conversation_viewer.current_composer != null) {
+ this.conversation_viewer.current_composer.activate_close_action();
+ }
+ }
+
private void on_offline_infobar_response() {
this.info_bars.remove(this.offline_infobar);
}
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 37e93fb4..17430021 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1361,6 +1361,11 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
}
+ /* Activate the close action */
+ public void activate_close_action() {
+ this.actions.activate_action(ACTION_CLOSE, null);
+ }
+
internal void set_mode(PresentationMode new_mode) {
this.current_mode = new_mode;
this.header.set_mode(new_mode);
diff --git a/ui/application-main-window.ui b/ui/application-main-window.ui
index fe66491b..cbaacbcf 100644
--- a/ui/application-main-window.ui
+++ b/ui/application-main-window.ui
@@ -29,6 +29,8 @@
<property name="can_focus">True</property>
<property name="can_swipe_back">True</property>
<property name="transition_type">over</property>
+ <signal name="notify::visible-child" handler="on_main_leaflet_visible_child_changed" swapped="no"/>
+ <signal name="notify::child-transition-running" handler="on_main_leaflet_visible_child_changed" swapped="no"/>
<child>
<object class="HdyLeaflet" id="conversations_leaflet">
<property name="visible">True</property>
--
2.29.2

View File

@ -0,0 +1,34 @@
From f8f223da46716a0a888ef5a5a0840fb69dbc7067 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Wed, 7 Oct 2020 10:36:50 +0200
Subject: [PATCH 061/124] main-window: Block forward navigation when viewer is
empty
Empty viewer includes: - no selected conversation
- no conversation in folder
- selected more then one conversation
---
src/client/application/application-main-window.vala | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index e1e55d0e..3b7c29e7 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -1811,8 +1811,11 @@ public class Application.MainWindow :
conversations_leaflet.navigate(Hdy.NavigationDirection.FORWARD);
focus = this.conversation_list_view;
} else {
- main_leaflet.navigate(Hdy.NavigationDirection.FORWARD);
- focus = this.conversation_viewer.visible_child;
+ if (this.main_toolbar.selected_conversations == 1 &&
+ this.selected_folder.properties.email_total > 0) {
+ main_leaflet.navigate(Hdy.NavigationDirection.FORWARD);
+ focus = this.conversation_viewer.visible_child;
+ }
}
}
} else if (focus != null) {
--
2.29.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
From 51b8c501be7d952e0023a72b24c91254b98fc48c Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 10 Oct 2020 23:55:02 +1100
Subject: [PATCH 063/124] Util.Email: Use a single unambiguous date format for
reply quote dates
Rather than using the UI pref for 12/24h clocks, use a single format
string that should be reasonably unambiguous and that includes the
time zone.
Fixes #888
---
src/client/composer/composer-widget.vala | 9 +--
src/client/util/util-email.vala | 78 ++++++++++++++----------
2 files changed, 49 insertions(+), 38 deletions(-)
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 37e93fb4..503727a9 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -740,7 +740,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
);
add_recipients_and_ids(type, full_context);
complete_quote = Util.Email.quote_email_for_reply(
- full_context, quote, this.config.clock_format, HTML
+ full_context, quote, HTML
);
if (!Geary.String.is_empty(quote)) {
this.top_posting = false;
@@ -1291,12 +1291,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
// Always use reply styling, since forward styling doesn't
// work for inline quotes
this.editor.body.insert_html(
- Util.Email.quote_email_for_reply(
- referred,
- to_quote,
- this.config.clock_format,
- Geary.RFC822.TextFormat.HTML
- )
+ Util.Email.quote_email_for_reply(referred, to_quote, HTML)
);
}
diff --git a/src/client/util/util-email.vala b/src/client/util/util-email.vala
index a18a45cf..4a1754a7 100644
--- a/src/client/util/util-email.vala
+++ b/src/client/util/util-email.vala
@@ -177,41 +177,57 @@ namespace Util.Email {
*/
public string quote_email_for_reply(Geary.Email email,
string? quote,
- Util.Date.ClockFormat clock_format,
Geary.RFC822.TextFormat format) {
- if (email.body == null && quote == null)
- return "";
-
string quoted = "";
+ if (email.body != null || quote != null) {
+ /// GLib g_date_time_format format string for the date and
+ /// time that a message being replied to was
+ /// received. This should be roughly similar to an RFC
+ /// 822-style date header value with optional additional
+ /// punctuation for readability. Note that this date may
+ /// be sent to someone in a different locale than the
+ /// sender, so should be unambiguous (for example, do not
+ /// use mm/dd/yyyy since it could be confused with
+ /// dd/mm/yyyy) and must include the time zone.
+ string date_format = _("%a, %b %-e %Y at %X %Z");
- string DATE_FORMAT = Util.Date.get_full_date(clock_format);
-
- if (email.date != null && email.from != null) {
- /// The quoted header for a message being replied to.
- /// %1$s will be substituted for the date, and %2$s will be substituted for
- /// the original sender.
- string QUOTED_LABEL = _("On %1$s, %2$s wrote:");
- quoted += QUOTED_LABEL.printf(email.date.value.format(DATE_FORMAT),
- Geary.RFC822.Utils.email_addresses_for_reply(email.from, format));
-
- } else if (email.from != null) {
- /// The quoted header for a message being replied to (in case the date is not known).
- /// %s will be replaced by the original sender.
- string QUOTED_LABEL = _("%s wrote:");
- quoted += QUOTED_LABEL.printf(Geary.RFC822.Utils.email_addresses_for_reply(email.from, format));
-
- } else if (email.date != null) {
- /// The quoted header for a message being replied to (in case the sender is not known).
- /// %s will be replaced by the original date
- string QUOTED_LABEL = _("On %s:");
- quoted += QUOTED_LABEL.printf(email.date.value.format(DATE_FORMAT));
- }
+ if (email.date != null && email.from != null) {
+ /// The quoted header for a message being replied to.
+ /// %1$s will be substituted for the date, and %2$s
+ /// will be substituted for the original sender.
+ string QUOTED_LABEL = _("On %1$s, %2$s wrote:");
+ quoted += QUOTED_LABEL.printf(
+ email.date.value.format(date_format),
+ Geary.RFC822.Utils.email_addresses_for_reply(
+ email.from, format
+ )
+ );
+ } else if (email.from != null) {
+ /// The quoted header for a message being replied to
+ /// (in case the date is not known). %s will be
+ /// replaced by the original sender.
+ string QUOTED_LABEL = _("%s wrote:");
+ quoted += QUOTED_LABEL.printf(
+ Geary.RFC822.Utils.email_addresses_for_reply(
+ email.from, format
+ )
+ );
+ } else if (email.date != null) {
+ /// The quoted header for a message being replied to
+ /// (in case the sender is not known). %s will be
+ /// replaced by the original date
+ string QUOTED_LABEL = _("On %s:");
+ quoted += QUOTED_LABEL.printf(
+ email.date.value.format(date_format)
+ );
+ }
- quoted += "<br />";
- try {
- quoted += quote_body(email, quote, true, format);
- } catch (Error err) {
- debug("Failed to quote body for replying: %s".printf(err.message));
+ quoted += "<br />";
+ try {
+ quoted += quote_body(email, quote, true, format);
+ } catch (Error err) {
+ debug("Failed to quote body for replying: %s".printf(err.message));
+ }
}
return quoted;
--
2.29.2

View File

@ -0,0 +1,49 @@
From 4be51e01c02f57fe68121603888ca012000d845d Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 10 Oct 2020 23:57:11 +1100
Subject: [PATCH 064/124] Util.Date: Remove now unused function
---
src/client/util/util-date.vala | 28 ----------------------------
1 file changed, 28 deletions(-)
diff --git a/src/client/util/util-date.vala b/src/client/util/util-date.vala
index 88948916..322a4cf2 100644
--- a/src/client/util/util-date.vala
+++ b/src/client/util/util-date.vala
@@ -208,32 +208,4 @@ public string get_clock_format(ClockFormat clock_format) {
return xlat_pretty_clocks[clock_format.to_index()];
}
-public string get_full_date(ClockFormat clock_format) {
- var value = "";
- switch (clock_format) {
- case TWELVE_HOURS:
- /// 12 hours format for datetime that a message being replied
- /// to was received See
- /// http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
- value = _("%a, %b %-e, %Y at %l:%M %P");
- break;
- case TWENTY_FOUR_HOURS:
- /// 24 hours format for the datetime that a message being
- /// replied to was received See
- /// http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
- value = _("%a, %b %-e, %Y at %H:%M");
- break;
- case LOCALE_DEFAULT:
- /// Format for the datetime that a message being replied to
- /// was received See
- /// http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
- value = _("%a, %b %-e, %Y at %X");
- break;
- case TOTAL:
- // noop
- break;
- }
- return value;
-}
-
}
--
2.29.2

View File

@ -0,0 +1,187 @@
From 70a40893a3f41483fbc607512d311f4c0c44c79a Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Thu, 8 Oct 2020 14:50:58 +0200
Subject: [PATCH 066/124] action-bar: Add an action bar to the conversations
list (2-panel)
This moves the actions from the headerbar to the action bar at the
bottom of the conversations list when multiple conversations are
selected. This changes is needed so that the user can still interact
with the conversations when folded.
This also hides the actions from the Headerbar and action bar when
no conversation is selected.
---
po/POTFILES.in | 2 +
.../application/application-main-window.vala | 19 +++++++++
.../components-conversation-action-bar.vala | 39 +++++++++++++++++++
src/client/meson.build | 1 +
ui/components-conversation-action-bar.ui | 22 +++++++++++
ui/org.gnome.Geary.gresource.xml | 1 +
6 files changed, 84 insertions(+)
create mode 100644 src/client/components/components-conversation-action-bar.vala
create mode 100644 ui/components-conversation-action-bar.ui
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7ef4e050..68e3ca34 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -40,6 +40,7 @@ src/client/client-action.vala
src/client/components/client-web-view.vala
src/client/components/components-attachment-pane.vala
src/client/components/components-conversation-actions.vala
+src/client/components/components-conversation-action-bar.vala
src/client/components/components-entry-undo.vala
src/client/components/components-in-app-notification.vala
src/client/components/components-info-bar-stack.vala
@@ -457,6 +458,7 @@ ui/components-attachment-pane.ui
ui/components-attachment-pane-menus.ui
ui/components-attachment-view.ui
ui/components-conversation-actions.ui
+ui/components-conversation-action-bar.ui
ui/components-in-app-notification.ui
ui/components-inspector-error-view.ui
ui/components-inspector-log-view.ui
diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala
index 20fc3758..0e6a89e1 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -351,6 +351,8 @@ public class Application.MainWindow :
[GtkChild]
private Gtk.Overlay overlay;
+ private Components.ConversationActionBar action_bar;
+
private Components.InfoBarStack info_bars =
new Components.InfoBarStack(SINGLE);
@@ -1308,6 +1310,12 @@ public class Application.MainWindow :
this.spinner.set_progress_monitor(progress_monitor);
this.status_bar.add(this.spinner);
this.status_bar.show_all();
+
+ // Action bar
+ this.action_bar = new Components.ConversationActionBar();
+ this.conversation_list_box.add_with_properties(action_bar,
+ "pack-type", Gtk.PackType.END,
+ "position", 0);
}
/** {@inheritDoc} */
@@ -1740,6 +1748,17 @@ public class Application.MainWindow :
);
this.update_context_dependent_actions.begin(sensitive);
+ switch (count) {
+ case NONE:
+ conversation_actions.take_ownership(null);
+ break;
+ case SINGLE:
+ this.main_toolbar.add_conversation_actions(this.conversation_actions);
+ break;
+ case MULTIPLE:
+ this.action_bar.add_conversation_actions(this.conversation_actions);
+ break;
+ }
}
private async void update_context_dependent_actions(bool sensitive) {
diff --git a/src/client/components/components-conversation-action-bar.vala b/src/client/components/components-conversation-action-bar.vala
new file mode 100644
index 00000000..cb574521
--- /dev/null
+++ b/src/client/components/components-conversation-action-bar.vala
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2020 Purism SPC
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+// Draws the conversation action bar.
+[GtkTemplate (ui = "/org/gnome/Geary/components-conversation-action-bar.ui")]
+public class Components.ConversationActionBar : Gtk.Revealer {
+ private ulong owner_notify;
+
+ [GtkChild]
+ private Gtk.Box action_box;
+
+ public ConversationActionBar() {
+ }
+
+ /**
+ * This takes ownership of the ConversationActions and places some of
+ * the buttons into the ActionBar.
+ */
+ public void add_conversation_actions(Components.ConversationActions actions) {
+ if (actions.owner == this)
+ return;
+
+ actions.take_ownership(this);
+ action_box.pack_start(actions.mark_copy_move_buttons, false, false);
+ action_box.pack_end(actions.archive_trash_delete_buttons, false, false);
+ reveal_child = true;
+ this.owner_notify = actions.notify["owner"].connect(() => {
+ if (actions.owner != this) {
+ reveal_child = false;
+ actions.disconnect (this.owner_notify);
+ }
+ });
+ }
+}
diff --git a/src/client/meson.build b/src/client/meson.build
index ed0d6b33..c0eb0c16 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -49,6 +49,7 @@ client_vala_sources = files(
'components/client-web-view.vala',
'components/components-attachment-pane.vala',
'components/components-conversation-actions.vala',
+ 'components/components-conversation-action-bar.vala',
'components/components-entry-undo.vala',
'components/components-info-bar-stack.vala',
'components/components-info-bar.vala',
diff --git a/ui/components-conversation-action-bar.ui b/ui/components-conversation-action-bar.ui
new file mode 100644
index 00000000..ae49683f
--- /dev/null
+++ b/ui/components-conversation-action-bar.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkImage" id="archive_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">mail-archive-symbolic</property>
+ </object>
+ <template class="ComponentsConversationActionBar" parent="GtkRevealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition_type">slide-up</property>
+ <child>
+ <object class="GtkBox" id="action_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="margin">6</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml
index 481bbff4..e064d331 100644
--- a/ui/org.gnome.Geary.gresource.xml
+++ b/ui/org.gnome.Geary.gresource.xml
@@ -14,6 +14,7 @@
<file compressed="true" preprocess="xml-stripblanks">components-attachment-pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-attachment-pane-menus.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-attachment-view.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks">components-conversation-action-bar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-conversation-actions.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-in-app-notification.ui</file>
<file compressed="true" preprocess="xml-stripblanks">components-inspector.ui</file>
--
2.29.2

View File

@ -0,0 +1,34 @@
From 0010550ad6f7d053b83f1857215294ce2b69f833 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Mon, 12 Oct 2020 23:22:47 +1100
Subject: [PATCH 067/124] Application.Client: Work around libhandy bug when
opening main windows
GNOME/libhandy#305
---
src/client/application/application-client.vala | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala
index 2bf4e094..46469086 100644
--- a/src/client/application/application-client.vala
+++ b/src/client/application/application-client.vala
@@ -892,6 +892,15 @@ public class Application.Client : Gtk.Application {
}
private MainWindow new_main_window(bool select_first_inbox) {
+ // Work around warning caused by GNOME/libhandy#305 which
+ // makes it a pita to run with G_DEBUG=fatal-warnings. Remove
+ // once the fix for that issue has been released and packaged.
+ GLib.Test.expect_message(
+ "GLib-GObject",
+ LEVEL_WARNING,
+ "g_object_weak_unref: couldn't find weak ref *"
+ );
+
MainWindow window = new MainWindow(this);
this.controller.register_window(window);
window.focus_in_event.connect(on_main_window_focus_in);
--
2.29.2

View File

@ -0,0 +1,330 @@
From 1ba2bd0f5ba655b38aff63d6332b0bb52c704119 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Wed, 26 Aug 2020 15:20:12 +1000
Subject: [PATCH 069/124] Util.JS: Support converting between JSC.Value and
GLib.Variant objects
Add `variant_to_value` and `value_to_variant` methods, document them
and add tests.
---
src/client/util/util-js.vala | 159 +++++++++++++++++++++++++++++
test/client/util/util-js-test.vala | 125 +++++++++++++++++++++++
2 files changed, 284 insertions(+)
diff --git a/src/client/util/util-js.vala b/src/client/util/util-js.vala
index 52c9428b..2f05a3e2 100644
--- a/src/client/util/util-js.vala
+++ b/src/client/util/util-js.vala
@@ -127,6 +127,165 @@ namespace Util.JS {
}
}
+ /**
+ * Converts a JS value to a GLib variant.
+ *
+ * Simple value objects (string, number, and Boolean values),
+ * arrays of these, and objects with these types as properties are
+ * supported. Arrays are converted to arrays of variants, and
+ * objects to dictionaries containing string keys and variant
+ * values. Null or undefined values are returned as an empty maybe
+ * variant type, since it is not possible to determine the actual
+ * type.
+ *
+ * Throws a type error if the given value's type is not supported.
+ */
+ public inline GLib.Variant value_to_variant(JSC.Value value)
+ throws Error {
+ if (value.is_null() || value.is_undefined()) {
+ return new GLib.Variant.maybe(GLib.VariantType.VARIANT, null);
+ }
+ if (value.is_boolean()) {
+ return new GLib.Variant.boolean(value.to_boolean());
+ }
+ if (value.is_number()) {
+ return new GLib.Variant.double(value.to_double());
+ }
+ if (value.is_string()) {
+ return new GLib.Variant.string(value.to_string());
+ }
+ if (value.is_array()) {
+ int len = to_int32(value.object_get_property("length"));
+ GLib.Variant[] values = new GLib.Variant[len];
+ for (int i = 0; i < len; i++) {
+ values[i] = new GLib.Variant.variant(
+ value_to_variant(value.object_get_property_at_index(i))
+ );
+ }
+ return new GLib.Variant.array(GLib.VariantType.VARIANT, values);
+ }
+ if (value.is_object()) {
+ GLib.VariantDict dict = new GLib.VariantDict();
+ string[] names = value.object_enumerate_properties();
+ if (names != null) {
+ foreach (var name in names) {
+ try {
+ dict.insert_value(
+ name,
+ new GLib.Variant.variant(
+ value_to_variant(
+ value.object_get_property(name)
+ )
+ )
+ );
+ } catch (Error.TYPE err) {
+ // ignored
+ }
+ }
+ }
+ return dict.end();
+ }
+ throw new Error.TYPE("Unsupported JS type: %s", value.to_string());
+ }
+
+ /**
+ * Converts a GLib variant to a JS value.
+ *
+ * Simple value objects (string, number, and Boolean values),
+ * arrays and tuples of these, and dictionaries with string keys
+ * are supported. Tuples and arrays are converted to JS arrays,
+ * and dictionaries or tuples containing dictionary entries are
+ * converted to JS objects.
+ *
+ * Throws a type error if the given variant's type is not supported.
+ */
+ public inline JSC.Value variant_to_value(JSC.Context context,
+ GLib.Variant variant)
+ throws Error.TYPE {
+ JSC.Value? value = null;
+ GLib.Variant.Class type = variant.classify();
+ if (type == MAYBE) {
+ GLib.Variant? maybe = variant.get_maybe();
+ if (maybe != null) {
+ value = variant_to_value(context, maybe);
+ } else {
+ value = new JSC.Value.null(context);
+ }
+ } else if (type == VARIANT) {
+ value = variant_to_value(context, variant.get_variant());
+ } else if (type == STRING) {
+ value = new JSC.Value.string(context, variant.get_string());
+ } else if (type == BOOLEAN) {
+ value = new JSC.Value.boolean(context, variant.get_boolean());
+ } else if (type == DOUBLE) {
+ value = new JSC.Value.number(context, variant.get_double());
+ } else if (type == INT64) {
+ value = new JSC.Value.number(context, (double) variant.get_int64());
+ } else if (type == INT32) {
+ value = new JSC.Value.number(context, (double) variant.get_int32());
+ } else if (type == INT16) {
+ value = new JSC.Value.number(context, (double) variant.get_int16());
+ } else if (type == UINT64) {
+ value = new JSC.Value.number(context, (double) variant.get_uint64());
+ } else if (type == UINT32) {
+ value = new JSC.Value.number(context, (double) variant.get_uint32());
+ } else if (type == UINT16) {
+ value = new JSC.Value.number(context, (double) variant.get_uint16());
+ } else if (type == BYTE) {
+ value = new JSC.Value.number(context, (double) variant.get_byte());
+ } else if (type == ARRAY ||
+ type == TUPLE) {
+ size_t len = variant.n_children();
+ if (len == 0) {
+ if (type == ARRAY ||
+ type == TUPLE) {
+ value = new JSC.Value.array_from_garray(context, null);
+ } else {
+ value = new JSC.Value.object(context, null, null);
+ }
+ } else {
+ var first = variant.get_child_value(0);
+ if (first.classify() == DICT_ENTRY) {
+ value = new JSC.Value.object(context, null, null);
+ for (size_t i = 0; i < len; i++) {
+ var entry = variant.get_child_value(i);
+ if (entry.classify() != DICT_ENTRY) {
+ throw new Error.TYPE(
+ "Variant mixes dict entries with others: %s",
+ variant.print(true)
+ );
+ }
+ var key = entry.get_child_value(0);
+ if (key.classify() != STRING) {
+ throw new Error.TYPE(
+ "Dict entry key is not a string: %s",
+ entry.print(true)
+ );
+ }
+ value.object_set_property(
+ key.get_string(),
+ variant_to_value(context, entry.get_child_value(1))
+ );
+ }
+ } else {
+ var values = new GLib.GenericArray<JSC.Value>((uint) len);
+ for (size_t i = 0; i < len; i++) {
+ values.add(
+ variant_to_value(context, variant.get_child_value(i))
+ );
+ }
+ value = new JSC.Value.array_from_garray(context, values);
+ }
+ }
+ }
+ if (value == null) {
+ throw new Error.TYPE(
+ "Unsupported variant type %s", variant.print(true)
+ );
+ }
+ return value;
+ }
+
/**
* Escapes a string so as to be safe to use as a JS string literal.
*
diff --git a/test/client/util/util-js-test.vala b/test/client/util/util-js-test.vala
index 1fbe5276..16a01d83 100644
--- a/test/client/util/util-js-test.vala
+++ b/test/client/util/util-js-test.vala
@@ -7,9 +7,23 @@
public class Util.JS.Test : TestCase {
+
+ private JSC.Context? context = null;
+
+
public Test() {
base("Util.JS.Test");
add_test("escape_string", escape_string);
+ add_test("to_variant", to_variant);
+ add_test("to_value", to_value);
+ }
+
+ public override void set_up() throws GLib.Error {
+ this.context = new JSC.Context();
+ }
+
+ public override void tear_down() throws GLib.Error {
+ this.context = null;
}
public void escape_string() throws GLib.Error {
@@ -21,4 +35,115 @@ public class Util.JS.Test : TestCase {
assert(Util.JS.escape_string("something…\n") == """something…\n""");
}
+
+ public void to_variant() throws GLib.Error {
+ assert_equal(
+ value_to_variant(new JSC.Value.null(this.context)).print(true),
+ "@mv nothing"
+ );
+ assert_equal(
+ value_to_variant(new JSC.Value.string(this.context, "test")).print(true),
+ "'test'"
+ );
+ assert_equal(
+ value_to_variant(new JSC.Value.number(this.context, 1.0)).print(true),
+ "1.0"
+ );
+ assert_equal(
+ value_to_variant(new JSC.Value.boolean(this.context, true)).print(true),
+ "true"
+ );
+ assert_equal(
+ value_to_variant(new JSC.Value.boolean(this.context, false)).print(true),
+ "false"
+ );
+
+ var value = new JSC.Value.array_from_garray(this.context, null);
+ assert_equal(
+ value_to_variant(value).print(true),
+ "@av []"
+ );
+ var array = new GLib.GenericArray<JSC.Value>();
+ array.add(new JSC.Value.string(this.context, "test"));
+ value = new JSC.Value.array_from_garray(this.context, array);
+ assert_equal(
+ value_to_variant(value).print(true),
+ "[<'test'>]"
+ );
+ value = new JSC.Value.object(this.context, null, null);
+ assert_equal(
+ value_to_variant(value).print(true),
+ "@a{sv} {}"
+ );
+ value.object_set_property(
+ "test", new JSC.Value.boolean(this.context, true)
+ );
+ assert_equal(
+ value_to_variant(value).print(true),
+ "{'test': <<true>>}"
+ );
+ }
+
+ public void to_value() throws GLib.Error {
+ var variant = new GLib.Variant.maybe(GLib.VariantType.STRING, null);
+ var value = variant_to_value(this.context, variant);
+ assert_true(value.is_null(), variant.print(true));
+
+ variant = new GLib.Variant.string("test");
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_string(), variant.print(true));
+ assert_equal(value.to_string(), "test", variant.print(true));
+
+ variant = new GLib.Variant.int32(42);
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_number(), variant.print(true));
+ assert_equal<int32?>(value.to_int32(), 42, variant.print(true));
+
+ variant = new GLib.Variant.double(42.0);
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_number(), variant.print(true));
+ assert_within(value.to_double(), 42.0, 0.0000001, variant.print(true));
+
+ variant = new GLib.Variant.boolean(true);
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_boolean(), variant.print(true));
+ assert_true(value.to_boolean(), variant.print(true));
+
+ variant = new GLib.Variant.boolean(false);
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_boolean(), variant.print(true));
+ assert_false(value.to_boolean(), variant.print(true));
+
+ variant = new GLib.Variant.strv({"test"});
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_array(), variant.print(true));
+ assert_true(
+ value.object_get_property_at_index(0).is_string(),
+ variant.print(true)
+ );
+ assert_equal(
+ value.object_get_property_at_index(0).to_string(),
+ "test",
+ variant.print(true)
+ );
+
+ var dict = new GLib.VariantDict();
+ variant = dict.end();
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_object(), variant.print(true));
+
+ dict = new GLib.VariantDict();
+ dict.insert_value("test", new GLib.Variant.boolean(true));
+ variant = dict.end();
+ value = variant_to_value(this.context, variant);
+ assert_true(value.is_object(), variant.print(true));
+ assert_true(
+ value.object_get_property("test").is_boolean(),
+ value.to_string()
+ );
+ assert_true(
+ value.object_get_property("test").to_boolean(),
+ value.to_string()
+ );
+ }
}
--
2.29.2

View File

@ -0,0 +1,644 @@
From ff565bc6efc83badbecfb48d2fbb457f4d2f681c Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Thu, 27 Aug 2020 12:12:22 +1000
Subject: [PATCH 070/124] Components.WebView: Convert to using messages for JS
method invocation
Use WebKitGTK UserMessage objects for invoking JS methods rather than
serialising to JS strings and running those. This is possibly slightly
less efficient, but removes the onus on serialising to and parsing from
JS and once switched over from message handlers to UserMessage objects
will be using a single uniform IPC interface for both.
---
.../components/components-web-view.vala | 97 +++++++++++++++++--
src/client/composer/composer-web-view.vala | 67 ++++++-------
src/client/composer/composer-widget.vala | 19 ++--
.../conversation-web-view.vala | 15 ++-
src/client/util/util-js.vala | 36 ++++---
.../web-process/web-process-extension.vala | 53 ++++++++++
test/js/components-page-state-test.vala | 45 +++++++++
ui/components-web-view.js | 10 ++
8 files changed, 268 insertions(+), 74 deletions(-)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index 4bda1c11..368b6a8d 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -370,9 +370,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
* Returns the view's content as an HTML string.
*/
public async string? get_html() throws Error {
- return Util.JS.to_string(
- yield call(Util.JS.callable("geary.getHtml"), null)
- );
+ return yield call_returning<string?>(Util.JS.callable("getHtml"), null);
}
/**
@@ -410,7 +408,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
* Load any remote images previously that were blocked.
*/
public void load_remote_images() {
- this.call.begin(Util.JS.callable("geary.loadRemoteImages"), null);
+ this.call_void.begin(Util.JS.callable("loadRemoteImages"), null);
}
/**
@@ -455,21 +453,100 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
public new async void set_editable(bool enabled,
Cancellable? cancellable)
throws Error {
- yield call(
- Util.JS.callable("geary.setEditable").bool(enabled), cancellable
+ yield call_void(
+ Util.JS.callable("setEditable").bool(enabled), cancellable
);
}
/**
* Invokes a {@link Util.JS.Callable} on this web view.
+ *
+ * This calls the given callable on the `geary` object for the
+ * current view, any returned value are ignored.
*/
- protected async JSC.Value call(Util.JS.Callable target,
+ protected async void call_void(Util.JS.Callable target,
GLib.Cancellable? cancellable)
throws GLib.Error {
- WebKit.JavascriptResult result = yield run_javascript(
- target.to_string(), cancellable
+ yield send_message_to_page(
+ target.to_message(), cancellable
);
- return result.get_js_value();
+ }
+
+ /**
+ * Invokes a {@link Util.JS.Callable} on this web view.
+ *
+ * This calls the given callable on the `geary` object for the
+ * current view. The value returned by the call is returned by
+ * this method.
+ *
+ * The type parameter `T` must match the type returned by the
+ * call, else an error is thrown. Only simple nullable value types
+ * are supported for T, for more complex return types (arrays,
+ * dictionaries, etc) specify {@link GLib.Variant} for `T` and
+ * manually parse that.
+ */
+ protected async T call_returning<T>(Util.JS.Callable target,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ WebKit.UserMessage? response = yield send_message_to_page(
+ target.to_message(), cancellable
+ );
+ if (response == null) {
+ throw new Util.JS.Error.TYPE(
+ "Method call did not return a value: %s", target.to_string()
+ );
+ }
+ GLib.Variant? param = response.parameters;
+ T ret_value = null;
+ var ret_type = typeof(T);
+ if (ret_type == typeof(GLib.Variant)) {
+ ret_value = param;
+ } else {
+ if (param != null && param.get_type().is_maybe()) {
+ param = param.get_maybe();
+ }
+ if (param != null) {
+ // Since these replies are coming from JS via
+ // Util.JS.value_to_variant, they will only be one of
+ // string, double, bool, array or dict
+ var param_type = param.classify();
+ if (ret_type == typeof(string) && param_type == STRING) {
+ ret_value = param.get_string();
+ } else if (ret_type == typeof(bool) && param_type == BOOLEAN) {
+ ret_value = (bool?) param.get_boolean();
+ } else if (ret_type == typeof(int) && param_type == DOUBLE) {
+ ret_value = (int?) ((int) param.get_double());
+ } else if (ret_type == typeof(short) && param_type == DOUBLE) {
+ ret_value = (short?) ((short) param.get_double());
+ } else if (ret_type == typeof(char) && param_type == DOUBLE) {
+ ret_value = (char?) ((char) param.get_double());
+ } else if (ret_type == typeof(long) && param_type == DOUBLE) {
+ ret_value = (long?) ((long) param.get_double());
+ } else if (ret_type == typeof(int64) && param_type == DOUBLE) {
+ ret_value = (int64?) ((int64) param.get_double());
+ } else if (ret_type == typeof(uint) && param_type == DOUBLE) {
+ ret_value = (uint?) ((uint) param.get_double());
+ } else if (ret_type == typeof(uchar) && param_type == DOUBLE) {
+ ret_value = (uchar?) ((uchar) param.get_double());
+ } else if (ret_type == typeof(ushort) && param_type == DOUBLE) {
+ ret_value = (ushort?) ((ushort) param.get_double());
+ } else if (ret_type == typeof(ulong) && param_type == DOUBLE) {
+ ret_value = (ulong?) ((ulong) param.get_double());
+ } else if (ret_type == typeof(uint64) && param_type == DOUBLE) {
+ ret_value = (uint64?) ((uint64) param.get_double());
+ } else if (ret_type == typeof(double) && param_type == DOUBLE) {
+ ret_value = (double?) param.get_double();
+ } else if (ret_type == typeof(float) && param_type == DOUBLE) {
+ ret_value = (float?) ((float) param.get_double());
+ } else {
+ throw new Util.JS.Error.TYPE(
+ "%s is not a supported type for %s",
+ ret_type.name(), param_type.to_string()
+ );
+ }
+ }
+ }
+ return ret_value;
}
/**
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index f8ecccf6..24a2740c 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -202,8 +202,8 @@ public class Composer.WebView : Components.WebView {
* Returns the view's content as HTML without being cleaned.
*/
public async string? get_html_for_draft() throws Error {
- return Util.JS.to_string(
- yield call(Util.JS.callable("geary.getHtml").bool(false), null)
+ return yield call_returning<string?>(
+ Util.JS.callable("getHtml").bool(false), null
);
}
@@ -213,8 +213,8 @@ public class Composer.WebView : Components.WebView {
public void set_rich_text(bool enabled) {
this.is_rich_text = enabled;
if (this.is_content_loaded) {
- this.call.begin(
- Util.JS.callable("geary.setRichText").bool(enabled), null
+ this.call_void.begin(
+ Util.JS.callable("setRichText").bool(enabled), null
);
}
}
@@ -223,14 +223,14 @@ public class Composer.WebView : Components.WebView {
* Undoes the last edit operation.
*/
public void undo() {
- this.call.begin(Util.JS.callable("geary.undo"), null);
+ this.call_void.begin(Util.JS.callable("undo"), null);
}
/**
* Redoes the last undone edit operation.
*/
public void redo() {
- this.call.begin(Util.JS.callable("geary.redo"), null);
+ this.call_void.begin(Util.JS.callable("redo"), null);
}
/**
@@ -239,9 +239,9 @@ public class Composer.WebView : Components.WebView {
* Returns an id to be used to refer to the selection in
* subsequent calls.
*/
- public async string save_selection() throws Error {
- return Util.JS.to_string(
- yield call(Util.JS.callable("geary.saveSelection"), null)
+ public async string? save_selection() throws Error {
+ return yield call_returning<string?>(
+ Util.JS.callable("saveSelection"), null
);
}
@@ -249,9 +249,7 @@ public class Composer.WebView : Components.WebView {
* Removes a saved selection.
*/
public void free_selection(string id) {
- this.call.begin(
- Util.JS.callable("geary.freeSelection").string(id), null
- );
+ this.call_void.begin(Util.JS.callable("freeSelection").string(id), null);
}
/**
@@ -357,9 +355,9 @@ public class Composer.WebView : Components.WebView {
* will be inserted wrapping the selection.
*/
public void insert_link(string href, string selection_id) {
- this.call.begin(
+ this.call_void.begin(
Util.JS.callable(
- "geary.insertLink"
+ "insertLink"
).string(href).string(selection_id),
null
);
@@ -373,8 +371,8 @@ public class Composer.WebView : Components.WebView {
* unlinked section.
*/
public void delete_link(string selection_id) {
- this.call.begin(
- Util.JS.callable("geary.deleteLink").string(selection_id),
+ this.call_void.begin(
+ Util.JS.callable("deleteLink").string(selection_id),
null
);
}
@@ -396,23 +394,23 @@ public class Composer.WebView : Components.WebView {
* Indents the line at the current text cursor location.
*/
public void indent_line() {
- this.call.begin(Util.JS.callable("geary.indentLine"), null);
+ this.call_void.begin(Util.JS.callable("indentLine"), null);
}
public void insert_olist() {
- this.call.begin(Util.JS.callable("geary.insertOrderedList"), null);
+ this.call_void.begin(Util.JS.callable("insertOrderedList"), null);
}
public void insert_ulist() {
- this.call.begin(Util.JS.callable("geary.insertUnorderedList"), null);
+ this.call_void.begin(Util.JS.callable("insertUnorderedList"), null);
}
/**
* Updates the signature block if it has not been deleted.
*/
public new void update_signature(string signature) {
- this.call.begin(
- Util.JS.callable("geary.updateSignature").string(signature), null
+ this.call_void.begin(
+ Util.JS.callable("updateSignature").string(signature), null
);
}
@@ -420,22 +418,21 @@ public class Composer.WebView : Components.WebView {
* Removes the quoted message (if any) from the composer.
*/
public void delete_quoted_message() {
- this.call.begin(Util.JS.callable("geary.deleteQuotedMessage"), null);
+ this.call_void.begin(Util.JS.callable("deleteQuotedMessage"), null);
}
/**
* Determines if the editor content contains an attachment keyword.
*/
- public async bool contains_attachment_keywords(string keyword_spec,
- string subject) {
+ public async bool? contains_attachment_keywords(string keyword_spec,
+ string subject) {
try {
- return Util.JS.to_bool(
- yield call(
- Util.JS.callable("geary.containsAttachmentKeyword")
- .string(keyword_spec)
- .string(subject),
- null)
- );
+ return yield call_returning<bool?>(
+ Util.JS.callable("containsAttachmentKeyword")
+ .string(keyword_spec)
+ .string(subject),
+ null
+ );
} catch (Error err) {
debug("Error checking or attachment keywords: %s", err.message);
return false;
@@ -449,7 +446,7 @@ public class Composer.WebView : Components.WebView {
* this.
*/
public async void clean_content() throws Error {
- this.call.begin(Util.JS.callable("geary.cleanContent"), null);
+ this.call_void.begin(Util.JS.callable("cleanContent"), null);
}
/**
@@ -459,10 +456,10 @@ public class Composer.WebView : Components.WebView {
const int MAX_BREAKABLE_LEN = 72; // F=F recommended line limit
const int MAX_UNBREAKABLE_LEN = 998; // SMTP line limit
- string body_text = Util.JS.to_string(
- yield call(Util.JS.callable("geary.getText"), null)
+ string? body_text = yield call_returning<string?>(
+ Util.JS.callable("getText"), null
);
- string[] lines = body_text.split("\n");
+ string[] lines = (body_text ?? "").split("\n");
GLib.StringBuilder flowed = new GLib.StringBuilder.sized(body_text.length);
foreach (string line in lines) {
// Strip trailing whitespace, so it doesn't look like a
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 4c4d0caf..9148a88e 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1450,15 +1450,16 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
confirmation = _("Send message with an empty subject?");
} else if (!has_body && !has_attachment) {
confirmation = _("Send message with an empty body?");
- } else if (!has_attachment &&
- yield this.editor.body.contains_attachment_keywords(
- string.join(
- "|",
- ATTACHMENT_KEYWORDS,
- ATTACHMENT_KEYWORDS_LOCALISED
- ),
- this.subject)) {
- confirmation = _("Send message without an attachment?");
+ } else if (!has_attachment) {
+ var keywords = string.join(
+ "|", ATTACHMENT_KEYWORDS, ATTACHMENT_KEYWORDS_LOCALISED
+ );
+ var contains = yield this.editor.body.contains_attachment_keywords(
+ keywords, this.subject
+ );
+ if (contains != null && contains) {
+ confirmation = _("Send message without an attachment?");
+ }
}
if (confirmation != null) {
ConfirmationDialog dialog = new ConfirmationDialog(container.top_window,
diff --git a/src/client/conversation-viewer/conversation-web-view.vala b/src/client/conversation-viewer/conversation-web-view.vala
index ffa36394..d77af642 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -89,20 +89,18 @@ public class ConversationWebView : Components.WebView {
* Returns the current selection, for prefill as find text.
*/
public async string? get_selection_for_find() throws Error{
- JSC.Value result = yield call(
- Util.JS.callable("geary.getSelectionForFind"), null
+ return yield call_returning<string?>(
+ Util.JS.callable("getSelectionForFind"), null
);
- return Util.JS.to_string(result);
}
/**
* Returns the current selection, for quoting in a message.
*/
public async string? get_selection_for_quoting() throws Error {
- JSC.Value result = yield call(
- Util.JS.callable("geary.getSelectionForQuoting"), null
+ return yield call_returning<string?>(
+ Util.JS.callable("getSelectionForQuoting"), null
);
- return Util.JS.to_string(result);
}
/**
@@ -110,10 +108,9 @@ public class ConversationWebView : Components.WebView {
*/
public async int? get_anchor_target_y(string anchor_body)
throws GLib.Error {
- JSC.Value result = yield call(
- Util.JS.callable("geary.getAnchorTargetY").string(anchor_body), null
+ return yield call_returning<int?>(
+ Util.JS.callable("getAnchorTargetY").string(anchor_body), null
);
- return (int) Util.JS.to_int32(result);
}
/**
diff --git a/src/client/util/util-js.vala b/src/client/util/util-js.vala
index 2f05a3e2..d2ce9f2e 100644
--- a/src/client/util/util-js.vala
+++ b/src/client/util/util-js.vala
@@ -348,40 +348,54 @@ namespace Util.JS {
*/
public class Callable {
- private string base_name;
- private string[] safe_args = new string[0];
+ private string name;
+ private GLib.Variant[] args = {};
- public Callable(string base_name) {
- this.base_name = base_name;
+ public Callable(string name) {
+ this.name = name;
+ }
+
+ public WebKit.UserMessage to_message() {
+ GLib.Variant? args = null;
+ if (this.args.length == 1) {
+ args = this.args[0];
+ } else if (this.args.length > 1) {
+ args = new GLib.Variant.tuple(this.args);
+ }
+ return new WebKit.UserMessage(this.name, args);
}
public string to_string() {
- return base_name + "(" + global::string.joinv(",", safe_args) + ");";
+ string[] args = new string[this.args.length];
+ for (int i = 0; i < args.length; i++) {
+ args[i] = this.args[i].print(true);
+ }
+ return this.name + "(" + global::string.joinv(",", args) + ")";
}
public Callable string(string value) {
- add_param("\"" + escape_string(value) + "\"");
+ add_param(new GLib.Variant.string(value));
return this;
}
public Callable double(double value) {
- add_param(value.to_string());
+ add_param(new GLib.Variant.double(value));
return this;
}
public Callable int(int value) {
- add_param(value.to_string());
+ add_param(new GLib.Variant.int32(value));
return this;
}
public Callable bool(bool value) {
- add_param(value ? "true" : "false");
+ add_param(new GLib.Variant.boolean(value));
return this;
}
- private inline void add_param(string value) {
- this.safe_args += value;
+ private inline void add_param(GLib.Variant value) {
+ this.args += value;
}
}
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 4bba5154..86f7f44c 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -30,6 +30,10 @@ public void webkit_web_extension_initialize_with_user_data(WebKit.WebExtension e
*/
public class GearyWebExtension : Object {
+ private const string PAGE_STATE_OBJECT_NAME = "geary";
+ private const string MESSAGE_RETURN_VALUE_NAME = "__return__";
+ private const string MESSAGE_EXCEPTION_NAME = "__exception__";
+
private const string[] ALLOWED_SCHEMES = { "cid", "geary", "data", "blob" };
private const string REMOTE_LOAD_VAR = "_gearyAllowRemoteResourceLoads";
@@ -157,6 +161,55 @@ public class GearyWebExtension : Object {
page.get_editor().selection_changed.connect(() => {
selection_changed(page);
});
+ page.user_message_received.connect(on_page_message_received);
+ }
+
+ private bool on_page_message_received(WebKit.WebPage page,
+ WebKit.UserMessage message) {
+ WebKit.Frame frame = page.get_main_frame();
+ JSC.Context context = frame.get_js_context();
+ JSC.Value page_state = context.get_value(PAGE_STATE_OBJECT_NAME);
+
+ try {
+ JSC.Value[]? call_param = null;
+ GLib.Variant? message_param = message.parameters;
+ if (message_param != null) {
+ if (message_param.is_container()) {
+ size_t len = message_param.n_children();
+ call_param = new JSC.Value[len];
+ for (size_t i = 0; i < len; i++) {
+ call_param[i] = Util.JS.variant_to_value(
+ context,
+ message_param.get_child_value(i)
+ );
+ }
+ } else {
+ call_param = {
+ Util.JS.variant_to_value(context, message_param)
+ };
+ }
+ }
+
+ JSC.Value ret = page_state.object_invoke_methodv(
+ message.name, call_param
+ );
+
+ // Must send a reply, even for void calls, otherwise
+ // WebKitGTK will complain. So return a message return
+ // rain hail or shine.
+ // https://bugs.webkit.org/show_bug.cgi?id=215880
+
+ message.send_reply(
+ new WebKit.UserMessage(
+ MESSAGE_RETURN_VALUE_NAME,
+ Util.JS.value_to_variant(ret)
+ )
+ );
+ } catch (GLib.Error err) {
+ debug("Failed to handle message: %s", err.message);
+ }
+
+ return true;
}
}
diff --git a/test/js/components-page-state-test.vala b/test/js/components-page-state-test.vala
index 5ec75746..562c6cda 100644
--- a/test/js/components-page-state-test.vala
+++ b/test/js/components-page-state-test.vala
@@ -14,12 +14,24 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
base(config);
}
+ public new async void call_void(Util.JS.Callable callable)
+ throws GLib.Error {
+ yield base.call_void(callable, null);
+ }
+
+ public new async string call_returning(Util.JS.Callable callable)
+ throws GLib.Error {
+ return yield base.call_returning<string>(callable, null);
+ }
+
}
public PageStateTest() {
base("Components.PageStateTest");
add_test("content_loaded", content_loaded);
+ add_test("call_void", call_void);
+ add_test("call_returning", call_returning);
try {
WebView.load_resources(GLib.File.new_for_path("/tmp"));
@@ -45,6 +57,30 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
assert(content_loaded_triggered);
}
+ public void call_void() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ test_article.call_void.begin(
+ new Util.JS.Callable("testVoid"), this.async_completion
+ );
+ test_article.call_void.end(this.async_result());
+ assert_test_result("void");
+ }
+
+ public void call_returning() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ test_article.call_returning.begin(
+ new Util.JS.Callable("testReturn").string("check 1-2"),
+ this.async_completion
+ );
+ string ret = test_article.call_returning.end(this.async_result());
+ assert_equal(ret, "check 1-2");
+ assert_test_result("check 1-2");
+ }
+
protected override WebView set_up_test_view() {
WebKit.UserScript test_script;
test_script = new WebKit.UserScript(
@@ -60,4 +96,13 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
return view;
}
+ private void assert_test_result(string expected)
+ throws GLib.Error {
+ string? result = Util.JS.to_string(
+ run_javascript("geary.testResult")
+ .get_js_value()
+ );
+ assert_equal(result, expected);
+ }
+
}
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 80e86d7c..289abca0 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -87,6 +87,8 @@ PageState.prototype = {
window.addEventListener("transitionend", function(e) {
queuePreferredHeightUpdate();
}, false); // load does not bubble
+
+ this.testResult = null;
},
getPreferredHeight: function() {
// Return the scroll height of the HTML element since the BODY
@@ -184,5 +186,13 @@ PageState.prototype = {
this.hasSelection = hasSelection;
window.webkit.messageHandlers.selectionChanged.postMessage(hasSelection);
}
+ },
+ // Methods below are for unit tests.
+ testVoid: function() {
+ this.testResult = "void";
+ },
+ testReturn: function(value) {
+ this.testResult = value;
+ return value;
}
};
--
2.29.2

View File

@ -0,0 +1,266 @@
From c813aa5707acc5226a57dca82449dc709969d05a Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Thu, 27 Aug 2020 16:18:45 +1000
Subject: [PATCH 071/124] Components.WebView: Check for pass up exceptions when
calling JS code
Update web extension to check for errors when invoking page state
methods and pass a message back if found. Check for this, decode and
throw a vala error in the WebView if found.
---
.../components/components-web-view.vala | 56 ++++++++++++++---
.../web-process/web-process-extension.vala | 39 ++++++++++--
test/js/components-page-state-test.vala | 60 +++++++++++++++++++
ui/components-web-view.js | 4 ++
4 files changed, 146 insertions(+), 13 deletions(-)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index 368b6a8d..2b373170 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -26,6 +26,10 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
/** URI Scheme and delimiter for images loaded by Content-ID. */
public const string CID_URL_PREFIX = "cid:";
+ // Keep these in sync with GearyWebExtension
+ private const string MESSAGE_RETURN_VALUE_NAME = "__return__";
+ private const string MESSAGE_EXCEPTION_NAME = "__exception__";
+
// WebKit message handler names
private const string COMMAND_STACK_CHANGED = "commandStackChanged";
private const string CONTENT_LOADED = "contentLoaded";
@@ -467,9 +471,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
protected async void call_void(Util.JS.Callable target,
GLib.Cancellable? cancellable)
throws GLib.Error {
- yield send_message_to_page(
- target.to_message(), cancellable
- );
+ yield call_impl(target, cancellable);
}
/**
@@ -488,12 +490,10 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
protected async T call_returning<T>(Util.JS.Callable target,
GLib.Cancellable? cancellable)
throws GLib.Error {
- WebKit.UserMessage? response = yield send_message_to_page(
- target.to_message(), cancellable
- );
+ WebKit.UserMessage? response = yield call_impl(target, cancellable);
if (response == null) {
throw new Util.JS.Error.TYPE(
- "Method call did not return a value: %s", target.to_string()
+ "Method call %s did not return a value", target.to_string()
);
}
GLib.Variant? param = response.parameters;
@@ -612,6 +612,48 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
"monospace-font", SettingsBindFlags.DEFAULT);
}
+ private async WebKit.UserMessage? call_impl(Util.JS.Callable target,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ WebKit.UserMessage? response = yield send_message_to_page(
+ target.to_message(), cancellable
+ );
+ if (response != null) {
+ var response_name = response.name;
+ if (response_name == MESSAGE_EXCEPTION_NAME) {
+ var exception = new GLib.VariantDict(response.parameters);
+ var name = exception.lookup_value("name", GLib.VariantType.STRING) as string;
+ var message = exception.lookup_value("message", GLib.VariantType.STRING) as string;
+ var backtrace = exception.lookup_value("backtrace_string", GLib.VariantType.STRING) as string;
+ var source = exception.lookup_value("source_uri", GLib.VariantType.STRING) as string;
+ var line = exception.lookup_value("line_number", GLib.VariantType.UINT32);
+ var column = exception.lookup_value("column_number", GLib.VariantType.UINT32);
+
+ var log_message = "Method call %s raised %s exception at %s:%d:%d: %s".printf(
+ target.to_string(),
+ name ?? "unknown",
+ source ?? "unknown",
+ (line != null ? (int) line.get_uint32() : -1),
+ (column != null ? (int) column.get_uint32() : -1),
+ message ?? "unknown"
+ );
+ debug(log_message);
+ if (backtrace != null) {
+ debug(backtrace);
+ }
+
+ throw new Util.JS.Error.EXCEPTION(log_message);
+ } else if (response_name != MESSAGE_RETURN_VALUE_NAME) {
+ throw new Util.JS.Error.TYPE(
+ "Method call %s returned unknown name: %s",
+ target.to_string(),
+ response_name
+ );
+ }
+ }
+ return response;
+ }
+
private void handle_cid_request(WebKit.URISchemeRequest request) {
if (!handle_internal_response(request)) {
request.finish_error(new FileError.NOENT("Unknown CID"));
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 86f7f44c..7aa6dd3c 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -31,6 +31,8 @@ public void webkit_web_extension_initialize_with_user_data(WebKit.WebExtension e
public class GearyWebExtension : Object {
private const string PAGE_STATE_OBJECT_NAME = "geary";
+
+ // Keep these in sync with Components.WebView
private const string MESSAGE_RETURN_VALUE_NAME = "__return__";
private const string MESSAGE_EXCEPTION_NAME = "__exception__";
@@ -199,12 +201,37 @@ public class GearyWebExtension : Object {
// rain hail or shine.
// https://bugs.webkit.org/show_bug.cgi?id=215880
- message.send_reply(
- new WebKit.UserMessage(
- MESSAGE_RETURN_VALUE_NAME,
- Util.JS.value_to_variant(ret)
- )
- );
+ JSC.Exception? thrown = context.get_exception();
+ if (thrown != null) {
+ var detail = new GLib.VariantDict();
+ if (thrown.get_message() != null) {
+ detail.insert_value("name", new GLib.Variant.string(thrown.get_name()));
+ }
+ if (thrown.get_message() != null) {
+ detail.insert_value("message", new GLib.Variant.string(thrown.get_message()));
+ }
+ if (thrown.get_backtrace_string() != null) {
+ detail.insert_value("backtrace_string", new GLib.Variant.string(thrown.get_backtrace_string()));
+ }
+ if (thrown.get_source_uri() != null) {
+ detail.insert_value("source_uri", new GLib.Variant.string(thrown.get_source_uri()));
+ }
+ detail.insert_value("line_number", new GLib.Variant.uint32(thrown.get_line_number()));
+ detail.insert_value("column_number", new GLib.Variant.uint32(thrown.get_column_number()));
+ message.send_reply(
+ new WebKit.UserMessage(
+ MESSAGE_EXCEPTION_NAME,
+ detail.end()
+ )
+ );
+ } else {
+ message.send_reply(
+ new WebKit.UserMessage(
+ MESSAGE_RETURN_VALUE_NAME,
+ Util.JS.value_to_variant(ret)
+ )
+ );
+ }
} catch (GLib.Error err) {
debug("Failed to handle message: %s", err.message);
}
diff --git a/test/js/components-page-state-test.vala b/test/js/components-page-state-test.vala
index 562c6cda..bf952416 100644
--- a/test/js/components-page-state-test.vala
+++ b/test/js/components-page-state-test.vala
@@ -31,7 +31,9 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
base("Components.PageStateTest");
add_test("content_loaded", content_loaded);
add_test("call_void", call_void);
+ add_test("call_void_throws", call_void_throws);
add_test("call_returning", call_returning);
+ add_test("call_returning_throws", call_returning_throws);
try {
WebView.load_resources(GLib.File.new_for_path("/tmp"));
@@ -68,6 +70,35 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
assert_test_result("void");
}
+ public void call_void_throws() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ try {
+ test_article.call_void.begin(
+ new Util.JS.Callable("testThrow").string("void message"),
+ this.async_completion
+ );
+ test_article.call_void.end(this.async_result());
+ assert_not_reached();
+ } catch (Util.JS.Error.EXCEPTION err) {
+ assert_string(
+ err.message
+ ).contains(
+ "testThrow"
+ // WebKitGTK doesn't actually pass any details through:
+ // https://bugs.webkit.org/show_bug.cgi?id=215877
+ // ).contains(
+ // "Error"
+ // ).contains(
+ // "void message"
+ // ).contains(
+ // "components-web-view.js"
+ );
+ assert_test_result("void message");
+ }
+ }
+
public void call_returning() throws GLib.Error {
load_body_fixture("OHHAI");
var test_article = this.test_view as TestWebView;
@@ -81,6 +112,35 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
assert_test_result("check 1-2");
}
+ public void call_returning_throws() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ try {
+ test_article.call_returning.begin(
+ new Util.JS.Callable("testThrow").string("return message"),
+ this.async_completion
+ );
+ test_article.call_returning.end(this.async_result());
+ assert_not_reached();
+ } catch (Util.JS.Error.EXCEPTION err) {
+ assert_string(
+ err.message
+ ).contains(
+ "testThrow"
+ // WebKitGTK doesn't actually pass any details through:
+ // https://bugs.webkit.org/show_bug.cgi?id=215877
+ // ).contains(
+ // "Error"
+ // ).contains(
+ // "return message"
+ // ).contains(
+ // "components-web-view.js"
+ );
+ assert_test_result("return message");
+ }
+ }
+
protected override WebView set_up_test_view() {
WebKit.UserScript test_script;
test_script = new WebKit.UserScript(
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 289abca0..0f932a19 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -194,5 +194,9 @@ PageState.prototype = {
testReturn: function(value) {
this.testResult = value;
return value;
+ },
+ testThrow: function(value) {
+ this.testResult = value;
+ throw this.testResult;
}
};
--
2.29.2

View File

@ -0,0 +1,88 @@
From db69807836cb2485af0941a83f57451c034b21a0 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 09:44:46 +1000
Subject: [PATCH 072/124] GearyWebExtension: Add factory method for error user
messages
---
.../web-process/web-process-extension.vala | 56 +++++++++++++------
1 file changed, 38 insertions(+), 18 deletions(-)
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 7aa6dd3c..89d9a1e3 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -145,6 +145,37 @@ public class GearyWebExtension : Object {
return ret;
}
+ private WebKit.UserMessage to_exception_message(string? name,
+ string? message,
+ string? backtrace = null,
+ string? source = null,
+ int line_number = -1,
+ int column_number = -1) {
+ var detail = new GLib.VariantDict();
+ if (name != null) {
+ detail.insert_value("name", new GLib.Variant.string(name));
+ }
+ if (message != null) {
+ detail.insert_value("message", new GLib.Variant.string(message));
+ }
+ if (backtrace != null) {
+ detail.insert_value("backtrace", new GLib.Variant.string(backtrace));
+ }
+ if (source != null) {
+ detail.insert_value("source", new GLib.Variant.string(source));
+ }
+ if (line_number > 0) {
+ detail.insert_value("line_number", new GLib.Variant.uint32(line_number));
+ }
+ if (column_number > 0) {
+ detail.insert_value("column_number", new GLib.Variant.uint32(column_number));
+ }
+ return new WebKit.UserMessage(
+ MESSAGE_EXCEPTION_NAME,
+ detail.end()
+ );
+ }
+
private void on_page_created(WebKit.WebExtension extension,
WebKit.WebPage page) {
WebKit.Frame frame = page.get_main_frame();
@@ -203,25 +234,14 @@ public class GearyWebExtension : Object {
JSC.Exception? thrown = context.get_exception();
if (thrown != null) {
- var detail = new GLib.VariantDict();
- if (thrown.get_message() != null) {
- detail.insert_value("name", new GLib.Variant.string(thrown.get_name()));
- }
- if (thrown.get_message() != null) {
- detail.insert_value("message", new GLib.Variant.string(thrown.get_message()));
- }
- if (thrown.get_backtrace_string() != null) {
- detail.insert_value("backtrace_string", new GLib.Variant.string(thrown.get_backtrace_string()));
- }
- if (thrown.get_source_uri() != null) {
- detail.insert_value("source_uri", new GLib.Variant.string(thrown.get_source_uri()));
- }
- detail.insert_value("line_number", new GLib.Variant.uint32(thrown.get_line_number()));
- detail.insert_value("column_number", new GLib.Variant.uint32(thrown.get_column_number()));
message.send_reply(
- new WebKit.UserMessage(
- MESSAGE_EXCEPTION_NAME,
- detail.end()
+ to_exception_message(
+ thrown.get_name(),
+ thrown.get_message(),
+ thrown.get_backtrace_string(),
+ thrown.get_source_uri(),
+ (int) thrown.get_line_number(),
+ (int) thrown.get_column_number()
)
);
} else {
--
2.29.2

View File

@ -0,0 +1,223 @@
From 6162785d997fcfa4efaf6ec83670b2fab8cca6bd Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 09:49:46 +1000
Subject: [PATCH 073/124] GearyWebExtension: Add support for sending messages
from JS to client
Define a vala-backed JS class in the extension and make that available
to pages when they are registered. Add some helper JS to PageState for
defining message sending functions. Listen for these in
Components.WebView and dispatch to the registered callback for it.
---
.../components/components-web-view.vala | 55 ++++++++++++++++
.../web-process/web-process-extension.vala | 63 +++++++++++++++++++
ui/components-web-view.js | 9 +++
3 files changed, 127 insertions(+)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index 2b373170..c746c441 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -198,6 +198,24 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
/** Delegate for UserContentManager message callbacks. */
public delegate void JavaScriptMessageHandler(WebKit.JavascriptResult js_result);
+ /**
+ * Delegate for message handler callbacks.
+ *
+ * @see register_message_callback
+ */
+ protected delegate void MessageCallback(GLib.Variant? parameters);
+
+ // Work around for not being able to put delegates in a Gee collection.
+ private class MessageCallable {
+
+ public unowned MessageCallback handler;
+
+ public MessageCallable(MessageCallback handler) {
+ this.handler = handler;
+ }
+
+ }
+
/**
* Determines if the view's content has been fully loaded.
*
@@ -263,6 +281,8 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
private Gee.List<ulong> registered_message_handlers =
new Gee.LinkedList<ulong>();
+ private Gee.Map<string,MessageCallable> message_handlers =
+ new Gee.HashMap<string,MessageCallable>();
private double webkit_reported_height = 0;
@@ -359,6 +379,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
this.user_content_manager.disconnect(id);
}
this.registered_message_handlers.clear();
+ this.message_handlers.clear();
base.destroy();
}
@@ -568,6 +589,14 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
}
}
+ /**
+ * Registers a callback for a specific WebKit user message.
+ */
+ protected void register_message_callback(string name,
+ MessageCallback handler) {
+ this.message_handlers.set(name, new MessageCallable(handler));
+ }
+
private void init(Application.Configuration config) {
// XXX get the allow prefix from the extension somehow
@@ -595,6 +624,8 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
SELECTION_CHANGED, on_selection_changed
);
+ this.user_message_received.connect(this.on_message_received);
+
// Manage zoom level, ensure it's sane
config.bind(Application.Configuration.CONVERSATION_VIEWER_ZOOM_KEY, this, "zoom_level");
if (this.zoom_level < ZOOM_MIN) {
@@ -803,6 +834,30 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
}
}
+ private bool on_message_received(WebKit.UserMessage message) {
+ if (message.name == MESSAGE_EXCEPTION_NAME) {
+ var detail = new GLib.VariantDict(message.parameters);
+ var name = detail.lookup_value("name", GLib.VariantType.STRING) as string;
+ var log_message = detail.lookup_value("message", GLib.VariantType.STRING) as string;
+ warning(
+ "Error sending message from JS: %s: %s",
+ name ?? "unknown",
+ log_message ?? "unknown"
+ );
+ } else if (this.message_handlers.has_key(message.name)) {
+ debug(
+ "Message received: %s(%s)",
+ message.name,
+ message.parameters != null ? message.parameters.print(true) : ""
+ );
+ MessageCallable callback = this.message_handlers.get(message.name);
+ callback.handler(message.parameters);
+ } else {
+ warning("Message with unknown handler received: %s", message.name);
+ }
+ return true;
+ }
+
}
// XXX this needs to be moved into the libsoup bindings
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 89d9a1e3..31f2b0f0 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -38,6 +38,8 @@ public class GearyWebExtension : Object {
private const string[] ALLOWED_SCHEMES = { "cid", "geary", "data", "blob" };
+ private const string EXTENSION_CLASS_VAR = "_GearyWebExtension";
+ private const string EXTENSION_CLASS_SEND = "send";
private const string REMOTE_LOAD_VAR = "_gearyAllowRemoteResourceLoads";
private WebKit.WebExtension extension;
@@ -180,6 +182,25 @@ public class GearyWebExtension : Object {
WebKit.WebPage page) {
WebKit.Frame frame = page.get_main_frame();
JSC.Context context = frame.get_js_context();
+
+ var extension_class = context.register_class(
+ this.get_type().name(),
+ null,
+ null,
+ null
+ );
+ extension_class.add_method(
+ EXTENSION_CLASS_SEND,
+ (instance, values) => {
+ return this.on_page_send_message(page, values);
+ },
+ GLib.Type.NONE
+ );
+ context.set_value(
+ EXTENSION_CLASS_VAR,
+ new JSC.Value.object(context, extension_class, extension_class)
+ );
+
context.set_value(
REMOTE_LOAD_VAR,
new JSC.Value.boolean(context, false)
@@ -259,4 +280,46 @@ public class GearyWebExtension : Object {
return true;
}
+ private bool on_page_send_message(WebKit.WebPage page,
+ GLib.GenericArray<JSC.Value> args) {
+ WebKit.UserMessage? message = null;
+ if (args.length > 0) {
+ var name = args.get(0).to_string();
+ GLib.Variant? parameters = null;
+ if (args.length > 1) {
+ JSC.Value param_value = args.get(1);
+ try {
+ int len = Util.JS.to_int32(
+ param_value.object_get_property("length")
+ );
+ if (len == 1) {
+ parameters = Util.JS.value_to_variant(
+ param_value.object_get_property_at_index(0)
+ );
+ } else if (len > 1) {
+ parameters = Util.JS.value_to_variant(param_value);
+ }
+ } catch (Util.JS.Error err) {
+ message = to_exception_message(
+ this.get_type().name(), err.message
+ );
+ }
+ }
+ if (message == null) {
+ message = new WebKit.UserMessage(name, parameters);
+ }
+ }
+ if (message == null) {
+ var log_message = "Not enough parameters for JS call to %s.%s()".printf(
+ EXTENSION_CLASS_VAR,
+ EXTENSION_CLASS_SEND
+ );
+ debug(log_message);
+ message = to_exception_message(this.get_type().name(), log_message);
+ }
+
+ page.send_message_to_view.begin(message, null);
+ return true;
+ }
+
}
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 0f932a19..35e82dfc 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -200,3 +200,12 @@ PageState.prototype = {
throw this.testResult;
}
};
+
+let MessageSender = function(name) {
+ return function() {
+ // Since typeof(arguments) == 'object', convert to an array so
+ // that Components.WebView.MessageCallback callbacks get
+ // arrays or tuples rather than dicts as arguments
+ _GearyWebExtension.send(name, Array.from(arguments));
+ };
+};
--
2.29.2

View File

@ -0,0 +1,275 @@
From 89453931bf6743049644274fc730a10e7bd2a53d Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 11:20:27 +1000
Subject: [PATCH 074/124] Util.Js: Improve JSC Value to GLib.Variant conversion
Stop needlessly wrapping object members and array elements in
variant variants.
Don't wrap object values in variants since the code is already using
vardicts for these. Return a variant array if a JS array contains values
of all the same type and don't wrap these in variants, else return
a tuple, which don't need to be wrapped either.
---
src/client/util/util-js.vala | 160 +++++++++++++++++++++--------
test/client/util/util-js-test.vala | 28 ++++-
2 files changed, 143 insertions(+), 45 deletions(-)
diff --git a/src/client/util/util-js.vala b/src/client/util/util-js.vala
index d2ce9f2e..095f9da4 100644
--- a/src/client/util/util-js.vala
+++ b/src/client/util/util-js.vala
@@ -1,5 +1,5 @@
/*
- * Copyright 2017,2019 Michael James Gratton <mike@vee.net>
+ * Copyright © 2017-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -25,6 +25,64 @@ namespace Util.JS {
TYPE
}
+ /** Supported types of JSC values. */
+ public enum JscType {
+
+ /** Specifies an unsupported value type. */
+ UNKNOWN,
+
+ /** Specifies a JavaScript `undefined` value. */
+ UNDEFINED,
+
+ /** Specifies a JavaScript `null` value. */
+ NULL,
+ FUNCTION,
+ STRING,
+ NUMBER,
+ BOOLEAN,
+ ARRAY,
+ CONSTRUCTOR,
+ OBJECT;
+
+ /**
+ * Determines the type of a JSC value.
+ *
+ * Returns the type of the given value, or {@link UNKNOWN} if
+ * it could not be determined.
+ */
+ public static JscType to_type(JSC.Value value) {
+ if (value.is_undefined()) {
+ return UNDEFINED;
+ }
+ if (value.is_null()) {
+ return NULL;
+ }
+ if (value.is_string()) {
+ return STRING;
+ }
+ if (value.is_number()) {
+ return NUMBER;
+ }
+ if (value.is_boolean()) {
+ return BOOLEAN;
+ }
+ if (value.is_array()) {
+ return ARRAY;
+ }
+ if (value.is_object()) {
+ return OBJECT;
+ }
+ if (value.is_function()) {
+ return FUNCTION;
+ }
+ if (value.is_constructor()) {
+ return CONSTRUCTOR;
+ }
+ return UNKNOWN;
+ }
+
+ }
+
/**
* Returns a JSC Value as a bool.
*
@@ -132,60 +190,80 @@ namespace Util.JS {
*
* Simple value objects (string, number, and Boolean values),
* arrays of these, and objects with these types as properties are
- * supported. Arrays are converted to arrays of variants, and
- * objects to dictionaries containing string keys and variant
- * values. Null or undefined values are returned as an empty maybe
- * variant type, since it is not possible to determine the actual
- * type.
+ * supported. Arrays containing objects of the same type are
+ * converted to arrays, otherwise they are converted to tuples,
+ * empty arrays are converted to the unit tuple, and objects are
+ * converted to vardict containing property names as keys and
+ * values. Null and undefined values are returned as an empty
+ * maybe variant type, since it is not possible to determine the
+ * actual type.
*
* Throws a type error if the given value's type is not supported.
*/
public inline GLib.Variant value_to_variant(JSC.Value value)
throws Error {
- if (value.is_null() || value.is_undefined()) {
- return new GLib.Variant.maybe(GLib.VariantType.VARIANT, null);
- }
- if (value.is_boolean()) {
- return new GLib.Variant.boolean(value.to_boolean());
- }
- if (value.is_number()) {
- return new GLib.Variant.double(value.to_double());
- }
- if (value.is_string()) {
- return new GLib.Variant.string(value.to_string());
- }
- if (value.is_array()) {
+ GLib.Variant? variant = null;
+ switch (JscType.to_type(value)) {
+ case UNDEFINED:
+ case NULL:
+ variant = new GLib.Variant.maybe(GLib.VariantType.VARIANT, null);
+ break;
+
+ case STRING:
+ variant = new GLib.Variant.string(value.to_string());
+ break;
+
+ case NUMBER:
+ variant = new GLib.Variant.double(value.to_double());
+ break;
+
+ case BOOLEAN:
+ variant = new GLib.Variant.boolean(value.to_boolean());
+ break;
+
+ case ARRAY:
int len = to_int32(value.object_get_property("length"));
- GLib.Variant[] values = new GLib.Variant[len];
- for (int i = 0; i < len; i++) {
- values[i] = new GLib.Variant.variant(
- value_to_variant(value.object_get_property_at_index(i))
- );
+ if (len == 0) {
+ variant = new GLib.Variant.tuple({});
+ } else {
+ JSC.Value element = value.object_get_property_at_index(0);
+ var first_type = JscType.to_type(element);
+ var all_same_type = true;
+ var values = new GLib.Variant[len];
+ values[0] = value_to_variant(element);
+ for (int i = 1; i < len; i++) {
+ element = value.object_get_property_at_index(i);
+ values[i] = value_to_variant(element);
+ all_same_type &= (first_type == JscType.to_type(element));
+ }
+ if (!all_same_type) {
+ variant = new GLib.Variant.tuple(values);
+ } else {
+ variant = new GLib.Variant.array(
+ values[0].get_type(), values
+ );
+ }
}
- return new GLib.Variant.array(GLib.VariantType.VARIANT, values);
- }
- if (value.is_object()) {
+ break;
+
+ case OBJECT:
GLib.VariantDict dict = new GLib.VariantDict();
string[] names = value.object_enumerate_properties();
if (names != null) {
foreach (var name in names) {
- try {
- dict.insert_value(
- name,
- new GLib.Variant.variant(
- value_to_variant(
- value.object_get_property(name)
- )
- )
- );
- } catch (Error.TYPE err) {
- // ignored
- }
+ dict.insert_value(
+ name,
+ value_to_variant(value.object_get_property(name))
+ );
}
}
- return dict.end();
+ variant = dict.end();
+ break;
+
+ default:
+ throw new Error.TYPE("Unsupported JS type: %s", value.to_string());
}
- throw new Error.TYPE("Unsupported JS type: %s", value.to_string());
+ return variant;
}
/**
diff --git a/test/client/util/util-js-test.vala b/test/client/util/util-js-test.vala
index 16a01d83..f1da043d 100644
--- a/test/client/util/util-js-test.vala
+++ b/test/client/util/util-js-test.vala
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Michael Gratton <mike@vee.net>
+ * Copyright © 2017-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -61,15 +61,35 @@ public class Util.JS.Test : TestCase {
var value = new JSC.Value.array_from_garray(this.context, null);
assert_equal(
value_to_variant(value).print(true),
- "@av []"
+ "()"
);
+
var array = new GLib.GenericArray<JSC.Value>();
array.add(new JSC.Value.string(this.context, "test"));
value = new JSC.Value.array_from_garray(this.context, array);
assert_equal(
value_to_variant(value).print(true),
- "[<'test'>]"
+ "['test']"
);
+
+ array = new GLib.GenericArray<JSC.Value>();
+ array.add(new JSC.Value.string(this.context, "test1"));
+ array.add(new JSC.Value.string(this.context, "test2"));
+ value = new JSC.Value.array_from_garray(this.context, array);
+ assert_equal(
+ value_to_variant(value).print(true),
+ "['test1', 'test2']"
+ );
+
+ array = new GLib.GenericArray<JSC.Value>();
+ array.add(new JSC.Value.string(this.context, "test"));
+ array.add(new JSC.Value.boolean(this.context, true));
+ value = new JSC.Value.array_from_garray(this.context, array);
+ assert_equal(
+ value_to_variant(value).print(true),
+ "('test', true)"
+ );
+
value = new JSC.Value.object(this.context, null, null);
assert_equal(
value_to_variant(value).print(true),
@@ -80,7 +100,7 @@ public class Util.JS.Test : TestCase {
);
assert_equal(
value_to_variant(value).print(true),
- "{'test': <<true>>}"
+ "{'test': <true>}"
);
}
--
2.29.2

View File

@ -0,0 +1,234 @@
From fb96676fbd4ab7f413071f18cbf444cd7a953b77 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 11:25:28 +1000
Subject: [PATCH 075/124] =?UTF-8?q?Components.WebView:=20Convert=20to=20us?=
=?UTF-8?q?ing=20messages=20for=20JS=20=E2=86=92=20client=20comms?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/components-web-view.vala | 79 +++++++++----------
ui/components-web-view.js | 25 +++---
2 files changed, 53 insertions(+), 51 deletions(-)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index c746c441..a5cdfe33 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2016-2019 Michael Gratton <mike@vee.net>
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2016-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -31,12 +31,12 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
private const string MESSAGE_EXCEPTION_NAME = "__exception__";
// WebKit message handler names
- private const string COMMAND_STACK_CHANGED = "commandStackChanged";
- private const string CONTENT_LOADED = "contentLoaded";
- private const string DOCUMENT_MODIFIED = "documentModified";
- private const string PREFERRED_HEIGHT_CHANGED = "preferredHeightChanged";
- private const string REMOTE_IMAGE_LOAD_BLOCKED = "remoteImageLoadBlocked";
- private const string SELECTION_CHANGED = "selectionChanged";
+ private const string COMMAND_STACK_CHANGED = "command_stack_changed";
+ private const string CONTENT_LOADED = "content_loaded";
+ private const string DOCUMENT_MODIFIED = "document_modified";
+ private const string PREFERRED_HEIGHT_CHANGED = "preferred_height_changed";
+ private const string REMOTE_IMAGE_LOAD_BLOCKED = "remote_image_load_blocked";
+ private const string SELECTION_CHANGED = "selection_changed";
private const double ZOOM_DEFAULT = 1.0;
private const double ZOOM_FACTOR = 0.1;
@@ -605,22 +605,22 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
warning("Web process crashed: %s", reason.to_string());
});
- register_message_handler(
+ register_message_callback(
COMMAND_STACK_CHANGED, on_command_stack_changed
);
- register_message_handler(
+ register_message_callback(
CONTENT_LOADED, on_content_loaded
);
- register_message_handler(
+ register_message_callback(
DOCUMENT_MODIFIED, on_document_modified
);
- register_message_handler(
+ register_message_callback(
PREFERRED_HEIGHT_CHANGED, on_preferred_height_changed
);
- register_message_handler(
+ register_message_callback(
REMOTE_IMAGE_LOAD_BLOCKED, on_remote_image_load_blocked
);
- register_message_handler(
+ register_message_callback(
SELECTION_CHANGED, on_selection_changed
);
@@ -783,12 +783,12 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
return false;
}
- private void on_preferred_height_changed(WebKit.JavascriptResult result) {
+ private void on_preferred_height_changed(GLib.Variant? parameters) {
double height = this.webkit_reported_height;
- try {
- height = Util.JS.to_double(result.get_js_value());
- } catch (Util.JS.Error err) {
- debug("Could not get preferred height: %s", err.message);
+ if (parameters != null && parameters.classify() == DOUBLE) {
+ height = parameters.get_double();
+ } else {
+ warning("Could not get JS preferred height");
}
if (this.webkit_reported_height != height) {
@@ -797,40 +797,39 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
}
}
- private void on_command_stack_changed(WebKit.JavascriptResult result) {
- try {
- string[] values =
- Util.JS.to_string(result.get_js_value()).split(",");
- command_stack_changed(values[0] == "true", values[1] == "true");
- } catch (Util.JS.Error err) {
- debug("Could not get command stack state: %s", err.message);
+ private void on_command_stack_changed(GLib.Variant? parameters) {
+ if (parameters != null &&
+ parameters.is_container() &&
+ parameters.n_children() == 2) {
+ GLib.Variant can_undo = parameters.get_child_value(0);
+ GLib.Variant can_redo = parameters.get_child_value(1);
+ command_stack_changed(
+ can_undo.classify() == BOOLEAN && can_undo.get_boolean(),
+ can_redo.classify() == BOOLEAN && can_redo.get_boolean()
+ );
+ } else {
+ warning("Could not get JS command stack state");
}
}
- private void on_document_modified(WebKit.JavascriptResult result) {
+ private void on_document_modified(GLib.Variant? parameters) {
document_modified();
}
- private void on_remote_image_load_blocked(WebKit.JavascriptResult result) {
+ private void on_remote_image_load_blocked(GLib.Variant? parameters) {
remote_image_load_blocked();
}
- private void on_content_loaded(WebKit.JavascriptResult result) {
+ private void on_content_loaded(GLib.Variant? parameters) {
this.is_content_loaded = true;
content_loaded();
}
- private void on_selection_changed(WebKit.JavascriptResult result) {
- try {
- bool has_selection = Util.JS.to_bool(result.get_js_value());
- // Avoid firing multiple notifies if the value hasn't
- // changed
- if (this.has_selection != has_selection) {
- this.has_selection = has_selection;
- }
- selection_changed(has_selection);
- } catch (Util.JS.Error err) {
- debug("Could not get selection content: %s", err.message);
+ private void on_selection_changed(GLib.Variant? parameters) {
+ if (parameters != null && parameters.classify() == BOOLEAN) {
+ selection_changed(parameters.get_boolean());
+ } else {
+ warning("Could not get JS selection value");
}
}
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 35e82dfc..29b6acd5 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Michael Gratton <mike@vee.net>
+ * Copyright © 2016-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -20,6 +20,13 @@ PageState.prototype = {
this.hasSelection = false;
this.lastPreferredHeight = 0;
+ this._selectionChanged = MessageSender("selection_changed");
+ this._contentLoaded = MessageSender("content_loaded");
+ this._remoteImageLoadBlocked = MessageSender("remote_image_load_blocked");
+ this._preferredHeightChanged = MessageSender("preferred_height_changed");
+ this._commandStackChanged = MessageSender("command_stack_changed");
+ this._documentModified = MessageSender("document_modified");
+
let state = this;
// Set up an observer to keep track of modifications made to
@@ -106,7 +113,7 @@ PageState.prototype = {
// be vaguegly correct when notifying of the HTML load
// completing.
this.updatePreferredHeight();
- window.webkit.messageHandlers.contentLoaded.postMessage(null);
+ this._contentLoaded();
},
loadRemoteImages: function() {
window._gearyAllowRemoteResourceLoads = true;
@@ -142,7 +149,7 @@ PageState.prototype = {
this.bodyObserver.disconnect();
},
remoteImageLoadBlocked: function() {
- window.webkit.messageHandlers.remoteImageLoadBlocked.postMessage(null);
+ this._remoteImageLoadBlocked();
},
/**
* Sends "preferredHeightChanged" message if it has changed.
@@ -160,9 +167,7 @@ PageState.prototype = {
// shrink again, leading to visual flicker.
if (this.isLoaded && height > 0 && height != this.lastPreferredHeight) {
this.lastPreferredHeight = height;
- window.webkit.messageHandlers.preferredHeightChanged.postMessage(
- height
- );
+ this._preferredHeightChanged(height);
}
},
checkCommandStack: function() {
@@ -172,19 +177,17 @@ PageState.prototype = {
if (canUndo != this.undoEnabled || canRedo != this.redoEnabled) {
this.undoEnabled = canUndo;
this.redoEnabled = canRedo;
- window.webkit.messageHandlers.commandStackChanged.postMessage(
- this.undoEnabled + "," + this.redoEnabled
- );
+ this._commandStackChanged(this.undoEnabled, this.redoEnabled);
}
},
documentModified: function(element) {
- window.webkit.messageHandlers.documentModified.postMessage(null);
+ this._documentModified();
},
selectionChanged: function() {
let hasSelection = !window.getSelection().isCollapsed;
if (this.hasSelection != hasSelection) {
this.hasSelection = hasSelection;
- window.webkit.messageHandlers.selectionChanged.postMessage(hasSelection);
+ this._selectionChanged(hasSelection);
}
},
// Methods below are for unit tests.
--
2.29.2

View File

@ -0,0 +1,185 @@
From 3655f4896f2b33f47a94c8e7dc4f9623ba42fc2e Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 11:38:06 +1000
Subject: [PATCH 076/124] =?UTF-8?q?Composer.WebView:=20Convert=20to=20usin?=
=?UTF-8?q?g=20messages=20for=20JS=20=E2=86=92=20client=20comms?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/client/composer/composer-web-view.vala | 75 ++++++++++------------
ui/composer-web-view.js | 18 ++++--
2 files changed, 45 insertions(+), 48 deletions(-)
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index 24a2740c..57b7e1e4 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -33,8 +33,8 @@ public class Composer.WebView : Components.WebView {
private const string SPACER = "<div><br /></div>";
// WebKit message handler names
- private const string CURSOR_CONTEXT_CHANGED = "cursorContextChanged";
- private const string DRAG_DROP_RECEIVED = "dragDropReceived";
+ private const string CURSOR_CONTEXT_CHANGED = "cursor_context_changed";
+ private const string DRAG_DROP_RECEIVED = "drag_drop_received";
/**
* Encapsulates editing-related state for a specific DOM node.
@@ -152,8 +152,8 @@ public class Composer.WebView : Components.WebView {
this.user_content_manager.add_style_sheet(WebView.app_style);
this.user_content_manager.add_script(WebView.app_script);
- register_message_handler(CURSOR_CONTEXT_CHANGED, on_cursor_context_changed);
- register_message_handler(DRAG_DROP_RECEIVED, on_drag_drop_received);
+ register_message_callback(CURSOR_CONTEXT_CHANGED, on_cursor_context_changed);
+ register_message_callback(DRAG_DROP_RECEIVED, on_drag_drop_received);
// XXX this is a bit of a hack given the docs for is_empty,
// above
@@ -530,50 +530,43 @@ public class Composer.WebView : Components.WebView {
return ret;
}
- private void on_cursor_context_changed(WebKit.JavascriptResult result) {
- try {
- cursor_context_changed(
- new EditContext(Util.JS.to_string(result.get_js_value()))
- );
- } catch (Util.JS.Error err) {
- debug("Could not get text cursor style: %s", err.message);
+ private void on_cursor_context_changed(GLib.Variant? parameters) {
+ if (parameters != null && parameters.classify() == STRING) {
+ cursor_context_changed(new EditContext(parameters as string));
+ } else {
+ warning("Could not get text cursor style");
}
}
/**
* Handle a dropped image
*/
- private void on_drag_drop_received(WebKit.JavascriptResult result) {
-
- try {
- JSC.Value object = result.get_js_value();
- string filename = Util.JS.to_string(
- Util.JS.get_property(object, "fileName")
- );
- string filename_unescaped = GLib.Uri.unescape_string(filename);
-
- string file_type = Util.JS.to_string(
- Util.JS.get_property(object, "fileType")
- );
-
- string content_base64 = Util.JS.to_string(
- Util.JS.get_property(object, "content")
- );
- uint8[] image = GLib.Base64.decode(content_base64);
-
- if (image.length == 0) {
- warning("%s is empty", filename);
- return;
- }
+ private void on_drag_drop_received(GLib.Variant? parameters) {
+ var dict = new GLib.VariantDict(parameters);
+ string file_name = dict.lookup_value(
+ "fileName", GLib.VariantType.STRING
+ ).get_string();
+ string file_name_unescaped = GLib.Uri.unescape_string(file_name);
+
+ string file_type = dict.lookup_value(
+ "fileType", GLib.VariantType.STRING
+ ).get_string();
+
+ string content_base64 = dict.lookup_value(
+ "content", GLib.VariantType.STRING
+ ).get_string();
+ uint8[] image = GLib.Base64.decode(content_base64);
+
+ if (image.length == 0) {
+ warning("%s is empty", file_name);
+ return;
+ }
- // A simple check to see if the file looks like an image. A problem here
- // will be this accepting types which won't be supported by WebKit
- // or recipients.
- if (file_type.index_of("image/") == 0) {
- image_file_dropped(filename_unescaped, file_type, image);
- }
- } catch (Util.JS.Error err) {
- debug("Could not get deceptive link param: %s", err.message);
+ // A simple check to see if the file looks like an image. A problem here
+ // will be this accepting types which won't be supported by WebKit
+ // or recipients.
+ if (file_type.index_of("image/") == 0) {
+ image_file_dropped(file_name_unescaped, file_type, image);
}
}
}
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js
index ca918990..4fe34ad0 100644
--- a/ui/composer-web-view.js
+++ b/ui/composer-web-view.js
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2016 Michael Gratton <mike@vee.net>
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2016-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -35,6 +35,9 @@ ComposerPageState.prototype = {
this.nextSelectionId = 0;
this.cursorContext = null;
+ this._cursorContextChanged = MessageSender("cursor_context_changed");
+ this._dragDropReceived = MessageSender("drag_drop_received");
+
document.addEventListener("click", function(e) {
if (e.target.tagName == "A") {
e.preventDefault();
@@ -99,7 +102,9 @@ ComposerPageState.prototype = {
}, true);
// Handle file drag & drop
- document.body.addEventListener("drop", state.handleFileDrop, true);
+ document.body.addEventListener("drop", function(e) {
+ state.handleFileDrop(e);
+ }, true);
document.body.addEventListener("allowDrop", function(e) {
ev.preventDefault();
}, true);
@@ -346,9 +351,7 @@ ComposerPageState.prototype = {
let newContext = new EditContext(cursor);
if (!newContext.equals(this.cursorContext)) {
this.cursorContext = newContext;
- window.webkit.messageHandlers.cursorContextChanged.postMessage(
- newContext.encode()
- );
+ this._cursorContextChanged(newContext.encode());
}
}
@@ -396,13 +399,14 @@ ComposerPageState.prototype = {
continue;
const reader = new FileReader();
+ const state = this;
reader.onload = (function(filename, imageType) { return function(loadEvent) {
// Remove prefixed file type and encoding type
var parts = loadEvent.target.result.split(",");
if (parts.length < 2)
return;
- window.webkit.messageHandlers.dragDropReceived.postMessage({
+ state._dragDropReceived({
fileName: encodeURIComponent(filename),
fileType: imageType,
content: parts[1]
--
2.29.2

View File

@ -0,0 +1,142 @@
From 7b0146274ccaad18115a66ded6753cb68bd22357 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 11:45:35 +1000
Subject: [PATCH 077/124] =?UTF-8?q?Conversation.WebView:=20Convert=20to=20?=
=?UTF-8?q?using=20messages=20for=20JS=20=E2=86=92=20client=20comms?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../conversation-web-view.vala | 71 +++++++++----------
ui/conversation-web-view.js | 4 +-
2 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/src/client/conversation-viewer/conversation-web-view.vala b/src/client/conversation-viewer/conversation-web-view.vala
index d77af642..a1ba21a6 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2017 Michael Gratton <mike@vee.net>
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2017-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -9,7 +9,7 @@
public class ConversationWebView : Components.WebView {
- private const string DECEPTIVE_LINK_CLICKED = "deceptiveLinkClicked";
+ private const string DECEPTIVE_LINK_CLICKED = "deceptive_link_clicked";
// Key codes we don't forward on to the super class on key press
// since we want to override them elsewhere, especially
@@ -221,48 +221,47 @@ public class ConversationWebView : Components.WebView {
}
private void init() {
- register_message_handler(
+ register_message_callback(
DECEPTIVE_LINK_CLICKED, on_deceptive_link_clicked
);
this.notify["preferred-height"].connect(() => queue_resize());
}
- private void on_deceptive_link_clicked(WebKit.JavascriptResult result) {
- try {
- JSC.Value object = result.get_js_value();
- uint reason = (uint) Util.JS.to_int32(
- Util.JS.get_property(object, "reason")
- );
-
- string href = Util.JS.to_string(
- Util.JS.get_property(object, "href")
- );
+ private void on_deceptive_link_clicked(GLib.Variant? parameters) {
+ var dict = new GLib.VariantDict(parameters);
+ uint reason = (uint) dict.lookup_value(
+ "reason", GLib.VariantType.DOUBLE
+ ).get_double();
- string text = Util.JS.to_string(
- Util.JS.get_property(object, "text")
- );
-
- JSC.Value js_location = Util.JS.get_property(object, "location");
+ string href = dict.lookup_value(
+ "href", GLib.VariantType.STRING
+ ).get_string();
- Gdk.Rectangle location = Gdk.Rectangle();
- location.x = Util.JS.to_int32(
- Util.JS.get_property(js_location, "x")
- );
- location.y = Util.JS.to_int32(
- Util.JS.get_property(js_location, "y")
- );
- location.width = Util.JS.to_int32(
- Util.JS.get_property(js_location, "width")
- );
- location.height = Util.JS.to_int32(
- Util.JS.get_property(js_location, "height")
- );
+ string text = dict.lookup_value(
+ "text", GLib.VariantType.STRING
+ ).get_string();
- deceptive_link_clicked((DeceptiveText) reason, text, href, location);
- } catch (Util.JS.Error err) {
- debug("Could not get deceptive link param: %s", err.message);
- }
+ Gdk.Rectangle location = Gdk.Rectangle();
+ var location_dict = new GLib.VariantDict(
+ dict.lookup_value("location", GLib.VariantType.VARDICT)
+ );
+ location.x = (int) location_dict.lookup_value(
+ "x", GLib.VariantType.DOUBLE
+ ).get_double();
+ location.y = (int) location_dict.lookup_value(
+ "y", GLib.VariantType.DOUBLE
+ ).get_double();
+ location.width = (int) location_dict.lookup_value(
+ "width", GLib.VariantType.DOUBLE
+ ).get_double();
+ location.height = (int) location_dict.lookup_value(
+ "height", GLib.VariantType.DOUBLE
+ ).get_double();
+
+ deceptive_link_clicked(
+ (DeceptiveText) reason, text, href, location
+ );
}
}
diff --git a/ui/conversation-web-view.js b/ui/conversation-web-view.js
index 451db288..1d730d47 100644
--- a/ui/conversation-web-view.js
+++ b/ui/conversation-web-view.js
@@ -26,6 +26,8 @@ ConversationPageState.prototype = {
init: function() {
PageState.prototype.init.apply(this, []);
+ this._deceptiveLinkClicked = MessageSender("deceptive_link_clicked");
+
let state = this;
document.addEventListener("click", function(e) {
if (e.target.tagName == "A" &&
@@ -267,7 +269,7 @@ ConversationPageState.prototype = {
let reason = ConversationPageState.isDeceptiveText(text, href);
if (reason != ConversationPageState.NOT_DECEPTIVE) {
cancelClick = true;
- window.webkit.messageHandlers.deceptiveLinkClicked.postMessage({
+ this._deceptiveLinkClicked({
reason: reason,
text: text,
href: href,
--
2.29.2

View File

@ -0,0 +1,138 @@
From 7950ce50c6bdb6ca77561a00d1afb9bc689278cf Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 11:59:11 +1000
Subject: [PATCH 078/124] GearyWebExtension: Untangle extension and JS
interaction a bit
Move selection changed event listener into JS so it doesn't have to
cross the JS/native boundary twice.
Move sending remote load blocked from JS to the extension since we can
do that directly now and again so the JS/native boundary doesn't need
to be double-crossed again.
---
.../web-process/web-process-extension.vala | 60 ++-----------------
ui/components-web-view.js | 8 +--
2 files changed, 8 insertions(+), 60 deletions(-)
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 31f2b0f0..6eed7746 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -77,7 +77,10 @@ public class GearyWebExtension : Object {
if (should_load_remote_images(page)) {
should_load = true;
} else {
- remote_image_load_blocked(page);
+ page.send_message_to_view.begin(
+ new WebKit.UserMessage("remote_image_load_blocked", null),
+ null
+ );
}
}
@@ -99,54 +102,6 @@ public class GearyWebExtension : Object {
return should_load;
}
- private void remote_image_load_blocked(WebKit.WebPage page) {
- WebKit.Frame frame = page.get_main_frame();
- JSC.Context context = frame.get_js_context();
- try {
- execute_script(
- context,
- "geary.remoteImageLoadBlocked();",
- GLib.Log.FILE,
- GLib.Log.METHOD,
- GLib.Log.LINE
- );
- } catch (Error err) {
- debug(
- "Error calling PageState::remoteImageLoadBlocked: %s",
- err.message
- );
- }
- }
-
- private void selection_changed(WebKit.WebPage page) {
- WebKit.Frame frame = page.get_main_frame();
- JSC.Context context = frame.get_js_context();
- try {
- execute_script(
- context,
- "geary.selectionChanged();",
- GLib.Log.FILE,
- GLib.Log.METHOD,
- GLib.Log.LINE
- );
- } catch (Error err) {
- debug("Error calling PageStates::selectionChanged: %s", err.message);
- }
- }
-
- private JSC.Value execute_script(JSC.Context context,
- string script,
- string file_name,
- string method_name,
- int line_number)
- throws Util.JS.Error {
- JSC.Value ret = context.evaluate_with_source_uri(
- script, -1, "geary:%s/%s".printf(file_name, method_name), line_number
- );
- Util.JS.check_exception(context);
- return ret;
- }
-
private WebKit.UserMessage to_exception_message(string? name,
string? message,
string? backtrace = null,
@@ -208,13 +163,6 @@ public class GearyWebExtension : Object {
page.console_message_sent.connect(on_console_message);
page.send_request.connect(on_send_request);
- // XXX investigate whether the earliest supported
- // version of WK supports the DOM "selectionchanged"
- // event, and if so use that rather that doing it in
- // here in the extension
- page.get_editor().selection_changed.connect(() => {
- selection_changed(page);
- });
page.user_message_received.connect(on_page_message_received);
}
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 29b6acd5..d0998a67 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -22,7 +22,6 @@ PageState.prototype = {
this._selectionChanged = MessageSender("selection_changed");
this._contentLoaded = MessageSender("content_loaded");
- this._remoteImageLoadBlocked = MessageSender("remote_image_load_blocked");
this._preferredHeightChanged = MessageSender("preferred_height_changed");
this._commandStackChanged = MessageSender("command_stack_changed");
this._documentModified = MessageSender("document_modified");
@@ -46,6 +45,10 @@ PageState.prototype = {
state.loaded();
});
+ document.addEventListener("selectionchange", function(e) {
+ state.selectionChanged();
+ });
+
// Coalesce multiple calls to updatePreferredHeight using a
// timeout to avoid the overhead of multiple JS messages sent
// to the app and hence view multiple resizes being queued.
@@ -148,9 +151,6 @@ PageState.prototype = {
stopBodyObserver: function() {
this.bodyObserver.disconnect();
},
- remoteImageLoadBlocked: function() {
- this._remoteImageLoadBlocked();
- },
/**
* Sends "preferredHeightChanged" message if it has changed.
*/
--
2.29.2

View File

@ -0,0 +1,43 @@
From eba82a1fbda18c2e567bd143862e7d687982b973 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 12:01:22 +1000
Subject: [PATCH 079/124] GearyWebExtension: Trivial code clean up
---
src/client/web-process/web-process-extension.vala | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 6eed7746..6785903e 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Michael Gratton <mike@vee.net>
+ * Copyright © 2016-2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -13,9 +13,9 @@ public void webkit_web_extension_initialize_with_user_data(WebKit.WebExtension e
bool logging_enabled = data.get_boolean();
Geary.Logging.init();
- GLib.Log.set_writer_func(Geary.Logging.default_log_writer);
if (logging_enabled) {
- Geary.Logging.log_to(stdout);
+ GLib.Log.set_writer_func(Geary.Logging.default_log_writer);
+ Geary.Logging.log_to(GLib.stdout);
}
debug("Initialising...");
@@ -50,7 +50,6 @@ public class GearyWebExtension : Object {
extension.page_created.connect(on_page_created);
}
- // XXX Conditionally enable while we still depend on WK2 <2.12
private void on_console_message(WebKit.WebPage page,
WebKit.ConsoleMessage message) {
string source = message.get_source_id();
--
2.29.2

View File

@ -0,0 +1,82 @@
From 47b134a04eeb502fadb80e6ae2340178645396bd Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 12:05:23 +1000
Subject: [PATCH 080/124] Components.WebView: Remove now-unused message handler
infrastructure
---
.../components/components-web-view.vala | 30 +------------------
1 file changed, 1 insertion(+), 29 deletions(-)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index a5cdfe33..904c5358 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -195,9 +195,6 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
}
- /** Delegate for UserContentManager message callbacks. */
- public delegate void JavaScriptMessageHandler(WebKit.JavascriptResult js_result);
-
/**
* Delegate for message handler callbacks.
*
@@ -279,8 +276,6 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
private Gee.Map<string,Geary.Memory.Buffer> internal_resources =
new Gee.HashMap<string,Geary.Memory.Buffer>();
- private Gee.List<ulong> registered_message_handlers =
- new Gee.LinkedList<ulong>();
private Gee.Map<string,MessageCallable> message_handlers =
new Gee.HashMap<string,MessageCallable>();
@@ -357,7 +352,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
* The new view will use the same WebProcess, settings and content
* manager as the given related view's.
*
- * @see WebKit.WebView.with_related_view
+ * @see WebKit.WebView.WebView.with_related_view
*/
protected WebView.with_related_view(Application.Configuration config,
WebView related) {
@@ -375,10 +370,6 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
}
public override void destroy() {
- foreach (ulong id in this.registered_message_handlers) {
- this.user_content_manager.disconnect(id);
- }
- this.registered_message_handlers.clear();
this.message_handlers.clear();
base.destroy();
}
@@ -570,25 +561,6 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
return ret_value;
}
- /**
- * Convenience function for registering and connecting JS messages.
- */
- protected inline void register_message_handler(string name,
- JavaScriptMessageHandler handler) {
- // XXX can't use the delegate directly, see b.g.o Bug
- // 604781. However the workaround below creates a circular
- // reference, causing WebView instances to leak. So to
- // work around that we need to record handler ids and
- // disconnect them when being destroyed.
- ulong id = this.user_content_manager.script_message_received[name].connect(
- (result) => { handler(result); }
- );
- this.registered_message_handlers.add(id);
- if (!this.user_content_manager.register_script_message_handler(name)) {
- debug("Failed to register script message handler: %s", name);
- }
- }
-
/**
* Registers a callback for a specific WebKit user message.
*/
--
2.29.2

View File

@ -0,0 +1,26 @@
From 92e842bf083d3185e6ee94b09eab710fb2374fac Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 12:06:07 +1000
Subject: [PATCH 081/124] ConversationViewer.ConversationMessage: Fix valadoc
warning
---
src/client/conversation-viewer/conversation-message.vala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/client/conversation-viewer/conversation-message.vala b/src/client/conversation-viewer/conversation-message.vala
index a11ece01..109c4a1c 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -666,7 +666,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
/**
* Adds a set of internal resources to web_view.
*
- * @see add_internal_resource
+ * @see Components.WebView.add_internal_resources
*/
public void add_internal_resources(Gee.Map<string,Geary.Memory.Buffer> res) {
if (this.web_view == null)
--
2.29.2

View File

@ -0,0 +1,103 @@
From 0609fbc3d7cec05a4e5fbf4c5ca9377ea802344b Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 12:06:36 +1000
Subject: [PATCH 082/124] Util.JS: Remove now-unused code
---
src/client/util/util-js.vala | 50 ------------------------------
test/client/util/util-js-test.vala | 11 -------
2 files changed, 61 deletions(-)
diff --git a/src/client/util/util-js.vala b/src/client/util/util-js.vala
index 095f9da4..193b3c7a 100644
--- a/src/client/util/util-js.vala
+++ b/src/client/util/util-js.vala
@@ -364,56 +364,6 @@ namespace Util.JS {
return value;
}
- /**
- * Escapes a string so as to be safe to use as a JS string literal.
- *
- * This does not append opening or closing quotes.
- */
- public string escape_string(string value) {
- StringBuilder builder = new StringBuilder.sized(value.length);
- for (int i = 0; i < value.length; i++) {
- if (value.valid_char(i)) {
- unichar c = value.get_char(i);
- switch (c) {
- case '\x00':
- builder.append("\x00");
- break;
- case '\'':
- builder.append("\\\'");
- break;
- case '"':
- builder.append("\\\"");
- break;
- case '\\':
- builder.append("\\\\");
- break;
- case '\n':
- builder.append("\\n");
- break;
- case '\r':
- builder.append("\\r");
- break;
- case '\x0b':
- builder.append("\x0b");
- break;
- case '\t':
- builder.append("\\t");
- break;
- case '\b':
- builder.append("\\b");
- break;
- case '\f':
- builder.append("\\f");
- break;
- default:
- builder.append_unichar(c);
- break;
- }
- }
- }
- return (string) builder.data;
- }
-
/**
* Convenience method for returning a new Callable instance.
*/
diff --git a/test/client/util/util-js-test.vala b/test/client/util/util-js-test.vala
index f1da043d..9fea3cd7 100644
--- a/test/client/util/util-js-test.vala
+++ b/test/client/util/util-js-test.vala
@@ -13,7 +13,6 @@ public class Util.JS.Test : TestCase {
public Test() {
base("Util.JS.Test");
- add_test("escape_string", escape_string);
add_test("to_variant", to_variant);
add_test("to_value", to_value);
}
@@ -26,16 +25,6 @@ public class Util.JS.Test : TestCase {
this.context = null;
}
- public void escape_string() throws GLib.Error {
- assert(Util.JS.escape_string("\n") == """\n""");
- assert(Util.JS.escape_string("\r") == """\r""");
- assert(Util.JS.escape_string("\t") == """\t""");
- assert(Util.JS.escape_string("\'") == """\'""");
- assert(Util.JS.escape_string("\"") == """\"""");
-
- assert(Util.JS.escape_string("something…\n") == """something…\n""");
- }
-
public void to_variant() throws GLib.Error {
assert_equal(
value_to_variant(new JSC.Value.null(this.context)).print(true),
--
2.29.2

View File

@ -0,0 +1,130 @@
From 1d80ed2034512aca7e355921c0b942d4cf651b94 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Fri, 28 Aug 2020 12:07:59 +1000
Subject: [PATCH 083/124] ComposerPageState: Use CSS for managing focus with
composer body parts
Now that the `:focus-within` pseudoclass is supported, use this rather
than some custom JS to update custom HTML classes. This also prevents
spurious mutation events from firing.
---
test/js/composer-page-state-test.vala | 24 ++++++++----------------
ui/composer-web-view.css | 6 +++---
ui/composer-web-view.js | 25 -------------------------
3 files changed, 11 insertions(+), 44 deletions(-)
diff --git a/test/js/composer-page-state-test.vala b/test/js/composer-page-state-test.vala
index 1a2e2df3..5a0a8b3c 100644
--- a/test/js/composer-page-state-test.vala
+++ b/test/js/composer-page-state-test.vala
@@ -11,7 +11,7 @@ class Composer.PageStateTest : Components.WebViewTestCase<Composer.WebView> {
"""<div id="geary-body" dir="auto">%s<div><br></div><div><br></div></div><div id="geary-signature" dir="auto"></div>""";
public const string DIRTY_BODY_TEMPLATE =
"""
-<div id="geary-body" dir="auto" class="geary-focus" contenteditable="true">%s<div><br></div><div><br></div></div>
+<div id="geary-body" dir="auto" contenteditable="true">%s<div><br></div><div><br></div></div>
<div id="geary-signature" class="geary-no-display" dir="auto" contenteditable="true"></div>
""";
public const string CLEAN_BODY_TEMPLATE = """<div id="geary-body" dir="auto">%s<div><br></div><div><br></div></div>""";
@@ -227,7 +227,7 @@ some text
}
}
- public void clean_content() throws Error {
+ public void clean_content() throws GLib.Error {
// XXX split these up into multiple tests
load_body_fixture("""
http://example1.com
@@ -257,20 +257,12 @@ unknown://example6.com
I can send email through smtp.gmail.com:587 or through <a href="https://www.gmail.com/">https://www.gmail.com/</a>
""";
- try {
- run_javascript("geary.cleanContent();");
- string result = Util.JS.to_string(
- run_javascript("window.document.body.innerHTML;")
- .get_js_value()
- );
- assert(result == DIRTY_BODY_TEMPLATE.printf(expected));
- } catch (Util.JS.Error err) {
- print("Util.JS.Error: %s\n", err.message);
- assert_not_reached();
- } catch (Error err) {
- print("WKError: %s\n", err.message);
- assert_not_reached();
- }
+ run_javascript("geary.cleanContent();");
+ string result = Util.JS.to_string(
+ run_javascript("window.document.body.innerHTML;")
+ .get_js_value()
+ );
+ assert_equal(result, DIRTY_BODY_TEMPLATE.printf(expected));
}
public void get_html() throws Error {
diff --git a/ui/composer-web-view.css b/ui/composer-web-view.css
index 3cecfb3b..07ae6869 100644
--- a/ui/composer-web-view.css
+++ b/ui/composer-web-view.css
@@ -43,12 +43,12 @@ body > div#geary-quote {
padding: 6px !important;
}
-body > div.geary-focus {
+body > div:focus-within {
background-color: white;
}
-body > div#geary-signature.geary-focus,
-body > div#geary-quote.geary-focus {
+body > div#geary-signature:focus-within,
+body > div#geary-quote:focus-within {
outline: 1px dashed #ccc !important;
}
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js
index 4fe34ad0..5ee4105e 100644
--- a/ui/composer-web-view.js
+++ b/ui/composer-web-view.js
@@ -123,7 +123,6 @@ ComposerPageState.prototype = {
// Focus within the HTML document
document.body.focus();
- this.updateFocusClass(this.bodyPart);
// Set text cursor at appropriate position
let cursor = document.getElementById("cursormarker");
@@ -354,30 +353,6 @@ ComposerPageState.prototype = {
this._cursorContextChanged(newContext.encode());
}
}
-
- while (cursor != null) {
- let parent = cursor.parentNode;
- if (parent == document.body) {
- this.updateFocusClass(cursor);
- break;
- }
- cursor = parent;
- }
- },
- /**
- * Work around WebKit note yet supporting :focus-inside pseudoclass.
- */
- updateFocusClass: function(newFocus) {
- if (this.focusedPart != null) {
- this.focusedPart.classList.remove("geary-focus");
- this.focusedPart = null;
- }
- if (newFocus == this.bodyPart ||
- newFocus == this.signaturePart ||
- newFocus == this.quotePart) {
- this.focusedPart = newFocus;
- this.focusedPart.classList.add("geary-focus");
- }
},
containedInPart: function(target) {
let inPart = false;
--
2.29.2

View File

@ -0,0 +1,26 @@
From 3f7c054db085a846b550c2cbc9fb71b352add2b9 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sat, 29 Aug 2020 12:57:08 +1000
Subject: [PATCH 084/124] build: Bump WebKitGTK min version to include
UserMessage support
---
meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index 240eacaa..9b55b24d 100644
--- a/meson.build
+++ b/meson.build
@@ -53,7 +53,7 @@ valac = meson.get_compiler('vala')
target_vala = '0.48.6'
target_glib = '2.64'
target_gtk = '3.24.7'
-target_webkit = '2.26'
+target_webkit = '2.28'
if not valac.version().version_compare('>=' + target_vala)
error('Vala does not meet minimum required version: ' + target_vala)
--
2.29.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
From 4db6d01e2356a5c9542ee394ecfaea5aaf6ccab8 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Tue, 13 Oct 2020 18:49:57 +1100
Subject: [PATCH 086/124] client: Remove perf relnote, it's not really that
noteworthy
---
desktop/org.gnome.Geary.appdata.xml.in.in | 8 --------
1 file changed, 8 deletions(-)
diff --git a/desktop/org.gnome.Geary.appdata.xml.in.in b/desktop/org.gnome.Geary.appdata.xml.in.in
index 471b69c4..7c1ddcab 100644
--- a/desktop/org.gnome.Geary.appdata.xml.in.in
+++ b/desktop/org.gnome.Geary.appdata.xml.in.in
@@ -88,14 +88,6 @@
<translation type="gettext">geary</translation>
<releases>
- <release version="3.40" date="">
- <description>
- <p>Enhancements included in this release:</p>
- <ul>
- <li>Conversation loading performance improvements</li>
- </ul>
- </description>
- </release>
<release version="3.38" date="2020-09-13">
<description>
<p>Enhancements included in this release:</p>
--
2.29.2

View File

@ -0,0 +1,973 @@
From 1f623bf100b78667e94a75d02a2a4a43512b827c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emin=20Tufan=20=C3=87etin?= <etcetin@gmail.com>
Date: Tue, 13 Oct 2020 13:48:40 +0000
Subject: [PATCH 087/124] Update Turkish translation
---
po/tr.po | 409 +++++++++++++++++++++++++++----------------------------
1 file changed, 201 insertions(+), 208 deletions(-)
diff --git a/po/tr.po b/po/tr.po
index f96666c5..51f95638 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -16,8 +16,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary.mainline\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
-"POT-Creation-Date: 2020-08-30 09:31+0000\n"
-"PO-Revision-Date: 2020-08-30 12:32+0300\n"
+"POT-Creation-Date: 2020-10-12 10:41+0000\n"
+"PO-Revision-Date: 2020-10-13 16:47+0300\n"
"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
"Language-Team: Turkish <gnome-turk@gnome.org>\n"
"Language: tr\n"
@@ -25,7 +25,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 2.4\n"
+"X-Generator: Poedit 2.4.1\n"
#: desktop/geary-attach.contract.desktop.in:3
msgid "Send by email"
@@ -40,7 +40,7 @@ msgstr "Dosyaları Geary kullanarak gönderin"
#: desktop/org.gnome.Geary.appdata.xml.in.in:11
#: desktop/org.gnome.Geary.desktop.in.in:3
#: src/client/accounts/accounts-editor-servers-pane.vala:560
-#: src/client/application/application-main-window.vala:608
+#: src/client/application/application-main-window.vala:628
msgid "Geary"
msgstr "Geary"
@@ -161,75 +161,34 @@ msgid "The last recorded height of the application window."
msgstr "Uygulama penceresinin kaydedilen son yüksekliği."
#: desktop/org.gnome.Geary.gschema.xml:26
-msgid "Position of folder list pane"
-msgstr "Klasör listesi bölmesinin konumu"
-
-#: desktop/org.gnome.Geary.gschema.xml:27
-msgid "Position of the folder list Paned grabber."
-msgstr "Klasör listesi bölmesi yakalayıcının konumu."
-
-#: desktop/org.gnome.Geary.gschema.xml:32
-msgid "Position of folder list pane when horizontal"
-msgstr "Klasör listesi bölmesinin yataykenki konumu"
-
-#: desktop/org.gnome.Geary.gschema.xml:33
-msgid ""
-"Position of the folder list Paned grabber in the horizontal orientation."
-msgstr "Yatay yönelimde klasör listesi bölmesi yakalayıcının konumu."
-
-#: desktop/org.gnome.Geary.gschema.xml:38
-msgid "Position of folder list pane when vertical"
-msgstr "Klasör listesi bölmesinin dikeykenki konumu"
-
-#: desktop/org.gnome.Geary.gschema.xml:39
-msgid "Position of the folder list Paned grabber in the vertical orientation."
-msgstr "Dikey yönelimde klasör listesi bölmesi yakalayıcının konumu."
-
-#: desktop/org.gnome.Geary.gschema.xml:44
-msgid "Orientation of the folder list pane"
-msgstr "Klasör listesi bölmesinin konumlandırması"
-
-#: desktop/org.gnome.Geary.gschema.xml:45
-msgid "True if the folder list Paned is in the horizontal orientation."
-msgstr "Eğer klasör listesi bölmesi yatay yönelimdeyse doğru."
-
-#: desktop/org.gnome.Geary.gschema.xml:50
msgid "Show/hide formatting toolbar"
msgstr "Biçimlendirme araç çubuğunu göster/gizle"
-#: desktop/org.gnome.Geary.gschema.xml:51
+#: desktop/org.gnome.Geary.gschema.xml:27
msgid "True if the formatting toolbar in the composer is shown."
msgstr "Eğer oluşturucudaki biçimlendirme araç çubuğu gösterilecekse doğru."
-#: desktop/org.gnome.Geary.gschema.xml:56
-msgid "Position of message list pane"
-msgstr "İleti listesi bölmesinin konumu"
-
-#: desktop/org.gnome.Geary.gschema.xml:57
-msgid "Position of the message list Paned grabber."
-msgstr "İleti listesi bölmesi yakalayıcının konumu."
-
-#: desktop/org.gnome.Geary.gschema.xml:62
+#: desktop/org.gnome.Geary.gschema.xml:32
msgid "Autoselect next message"
msgstr "Sonraki iletiyi kendiliğinden seç"
-#: desktop/org.gnome.Geary.gschema.xml:63
+#: desktop/org.gnome.Geary.gschema.xml:33
msgid "True if we should autoselect the next available conversation."
msgstr "Eğer sonraki uygun konuşmayı kendiliğinden seçmemiz gerekiyorsa doğru."
-#: desktop/org.gnome.Geary.gschema.xml:68
+#: desktop/org.gnome.Geary.gschema.xml:38
msgid "Display message previews"
msgstr "İleti ön izlemelerini göster"
-#: desktop/org.gnome.Geary.gschema.xml:69
+#: desktop/org.gnome.Geary.gschema.xml:39
msgid "True if we should display a short preview of each message."
msgstr "Her iletinin kısa bir ön izlemesini göstermemiz gerekiyorsa doğru."
-#: desktop/org.gnome.Geary.gschema.xml:74
+#: desktop/org.gnome.Geary.gschema.xml:44
msgid "Use single key shortcuts"
msgstr "Tek tuşlu kısayolları kullan"
-#: desktop/org.gnome.Geary.gschema.xml:75
+#: desktop/org.gnome.Geary.gschema.xml:45
msgid ""
"Enables shortcuts for email actions that do not require pressing <Ctrl> to "
"emulate those used by Gmail."
@@ -237,11 +196,11 @@ msgstr ""
"Gmailʼin kullandığına benzemek için eposta eylemlerinde <Ctrl>ʼye basmayı "
"gerektirmeyen kısayolları etkinleştirir."
-#: desktop/org.gnome.Geary.gschema.xml:82
+#: desktop/org.gnome.Geary.gschema.xml:52
msgid "Languages that shall be used in the spell checker"
msgstr "Yazım denetleyicide kullanılacak diller"
-#: desktop/org.gnome.Geary.gschema.xml:83
+#: desktop/org.gnome.Geary.gschema.xml:53
msgid ""
"A list of POSIX locales, with the empty list disabling spell checking and "
"the null list using desktop languages by default."
@@ -249,11 +208,11 @@ msgstr ""
"POSIX yerellerinin listesi, boş listeyle imla denetimi devre dışı bırakılır "
"ve butlan (null) listeyle öntanımlı olarak masaüstü dillerini kullanılır."
-#: desktop/org.gnome.Geary.gschema.xml:90
+#: desktop/org.gnome.Geary.gschema.xml:60
msgid "Languages that are displayed in the spell checker popover"
msgstr "Yazım denetleyici açılır penceresinde gösterilecek diller"
-#: desktop/org.gnome.Geary.gschema.xml:91
+#: desktop/org.gnome.Geary.gschema.xml:61
msgid ""
"List of languages that are always displayed in the popover of the spell "
"checker."
@@ -261,62 +220,62 @@ msgstr ""
"Yazım denetleyicinin açılır penceresinde her zaman gösterilecek dillerin "
"listesi."
-#: desktop/org.gnome.Geary.gschema.xml:96
+#: desktop/org.gnome.Geary.gschema.xml:66
msgid "Notify of new mail at startup"
msgstr "Başlangıçta yeni postanın bildirilmesi"
-#: desktop/org.gnome.Geary.gschema.xml:97
+#: desktop/org.gnome.Geary.gschema.xml:67
msgid "True to notify of new mail at startup."
msgstr "Başlangıçta yeni postaların bildirilmesi için doğru."
-#: desktop/org.gnome.Geary.gschema.xml:102
+#: desktop/org.gnome.Geary.gschema.xml:72
msgid "Ask when opening an attachment"
msgstr "Ek açarken sor"
-#: desktop/org.gnome.Geary.gschema.xml:103
+#: desktop/org.gnome.Geary.gschema.xml:73
msgid "True to ask when opening an attachment."
msgstr "Eki açarken sormak için doğru."
-#: desktop/org.gnome.Geary.gschema.xml:108
+#: desktop/org.gnome.Geary.gschema.xml:78
msgid "Whether to compose emails in HTML"
msgstr "E-postaların HTMLde oluşturulup oluşturulmayacağı"
-#: desktop/org.gnome.Geary.gschema.xml:109
+#: desktop/org.gnome.Geary.gschema.xml:79
msgid "True to compose emails in HTML; false for plain text."
msgstr "E-postaları HTMLde oluşturmak için doğru, düz metin için yanlış."
-#: desktop/org.gnome.Geary.gschema.xml:114
+#: desktop/org.gnome.Geary.gschema.xml:84
msgid "Advisory strategy for full-text searching"
msgstr "Tam metin arama için tavsiye niteliğinde izlem"
-#: desktop/org.gnome.Geary.gschema.xml:115
+#: desktop/org.gnome.Geary.gschema.xml:85
msgid ""
"Acceptable values are “exact”, “conservative”, “aggressive”, and “horizon”."
msgstr ""
"Kabul edilebilir değerler şunlardır: “exact” (birebir), "
"“conservative” (ılımlı), “aggressive” (sert) ve “horizon”."
-#: desktop/org.gnome.Geary.gschema.xml:120
+#: desktop/org.gnome.Geary.gschema.xml:90
msgid "Zoom of conversation viewer"
msgstr "Konuşma göstericisinin yakınlaşması"
-#: desktop/org.gnome.Geary.gschema.xml:121
+#: desktop/org.gnome.Geary.gschema.xml:91
msgid "The zoom to apply on the conservation view."
msgstr "Konuşma görünümünde uygulanacak yakınlaşma."
-#: desktop/org.gnome.Geary.gschema.xml:126
+#: desktop/org.gnome.Geary.gschema.xml:96
msgid "Size of detached composer window"
msgstr "Ayrılan oluşturucu penceresinin boyutu"
-#: desktop/org.gnome.Geary.gschema.xml:127
+#: desktop/org.gnome.Geary.gschema.xml:97
msgid "The last recorded size of the detached composer window."
msgstr "Ayrılmış oluşturucu penceresinin kaydedilen son boyutu."
-#: desktop/org.gnome.Geary.gschema.xml:132
+#: desktop/org.gnome.Geary.gschema.xml:102
msgid "Undo sending email delay"
msgstr "Eposta göndermeyi geri alma gecikmesi"
-#: desktop/org.gnome.Geary.gschema.xml:133
+#: desktop/org.gnome.Geary.gschema.xml:103
msgid ""
"The number of seconds to wait before sending an email. Set to zero or less "
"to disable."
@@ -324,29 +283,29 @@ msgstr ""
"Eposta gönderilmeden önce beklenecek saniye. Devre dışı bırakmak için sıfır "
"veya daha azına belirleyin."
-#: desktop/org.gnome.Geary.gschema.xml:139
+#: desktop/org.gnome.Geary.gschema.xml:109
msgid "Brief notification display time"
msgstr "Özet bildirim gösterim zamanı"
-#: desktop/org.gnome.Geary.gschema.xml:140
+#: desktop/org.gnome.Geary.gschema.xml:110
msgid ""
"The length of time in seconds for which brief notifications should be "
"displayed."
msgstr "Özet bildirimlerin gösterileceği zamanın saniye türünde uzunluğu."
-#: desktop/org.gnome.Geary.gschema.xml:146
+#: desktop/org.gnome.Geary.gschema.xml:116
msgid "List of optional plugins"
msgstr "İsteğe bağlı eklenti listesi"
-#: desktop/org.gnome.Geary.gschema.xml:147
+#: desktop/org.gnome.Geary.gschema.xml:117
msgid "Plugins listed here will be loaded on startup."
msgstr "Burada listelenen eklentiler başlangıçta yüklenecek."
-#: desktop/org.gnome.Geary.gschema.xml:152
+#: desktop/org.gnome.Geary.gschema.xml:122
msgid "Whether we migrated the old settings"
msgstr "Eski ayarları taşıyıp taşımayacağımız"
-#: desktop/org.gnome.Geary.gschema.xml:153
+#: desktop/org.gnome.Geary.gschema.xml:123
msgid ""
"False to check for the old “org.yorba.geary”-schema and copy its values."
msgstr ""
@@ -622,12 +581,12 @@ msgid_plural "%d days back"
msgstr[0] "%d gün öncesinden"
#: src/client/accounts/accounts-editor-list-pane.vala:255
-#: src/client/application/application-main-window.vala:2061
+#: src/client/application/application-main-window.vala:2129
msgid "Undo"
msgstr "Geri Al"
#: src/client/accounts/accounts-editor-list-pane.vala:264
-#: src/client/application/application-main-window.vala:2044
+#: src/client/application/application-main-window.vala:2112
msgid "Redo"
msgstr "Yinele"
@@ -722,7 +681,7 @@ msgstr "TLS"
#. account
#. Translators: An info bar button label
#: src/client/accounts/accounts-editor-row.vala:539
-#: src/client/application/application-main-window.vala:554
+#: src/client/application/application-main-window.vala:574
msgid "Login"
msgstr "Giriş"
@@ -974,13 +933,13 @@ msgstr ""
#. / Warning printed to the console when a deprecated
#. / command line option is used.
-#: src/client/application/application-client.vala:1047
+#: src/client/application/application-client.vala:1045
msgid "The `--hidden` option is deprecated and will be removed in the future."
msgstr "`--hidden` seçeneği terk edilmiştir ve gelecekte kaldırılacaktır."
#. / Command line warning, string substitution
#. / is the given argument
-#: src/client/application/application-client.vala:1080
+#: src/client/application/application-client.vala:1078
#, c-format
msgid "Unrecognised program argument: “%s”"
msgstr "Tanınmayan program argümanı: “%s”"
@@ -1165,54 +1124,54 @@ msgid "Email to %s discarded"
msgstr "Şun(lar)a gidecek eposta gözden çıkarıldı: %s"
#. Translators: An info bar status label
-#: src/client/application/application-main-window.vala:540
+#: src/client/application/application-main-window.vala:560
msgid "Working offline"
msgstr "Çevrim dışı çalışıyor"
#. Translators: An info bar description label
-#: src/client/application/application-main-window.vala:542
+#: src/client/application/application-main-window.vala:562
msgid "You will not be able to send or receive email until re-connected."
msgstr "Yeniden bağlanana dek e-posta gönderemez veya alamazsınız."
#. Translators: An info bar status label
-#: src/client/application/application-main-window.vala:549
+#: src/client/application/application-main-window.vala:569
msgid "Login problem"
msgstr "Giriş sorunu"
#. Translators: An info bar description label
-#: src/client/application/application-main-window.vala:551
+#: src/client/application/application-main-window.vala:571
msgid "An account has reported an incorrect login or password."
msgstr "Hesap yanlış bir giriş veya parola bildirdi."
#. Translators: An info bar button tool-tip
-#: src/client/application/application-main-window.vala:558
+#: src/client/application/application-main-window.vala:578
msgid "Retry login, you will be prompted for your password"
msgstr "Giriş yapmayı yeniden dene, parola girmeniz istenecek"
#. Translators: An info bar status label
-#: src/client/application/application-main-window.vala:565
+#: src/client/application/application-main-window.vala:585
msgid "Security problem"
msgstr "Güvenlik sorunu"
#. Translators: An info bar description label
-#: src/client/application/application-main-window.vala:567
+#: src/client/application/application-main-window.vala:587
msgid "An account has reported an untrusted server."
msgstr "Hesap, güvenilmeyen bir sunucu bildirdi."
#. Translators: An info bar button label
-#: src/client/application/application-main-window.vala:570
+#: src/client/application/application-main-window.vala:590
msgid "Check"
msgstr "Gözden geçir"
#. Translators: An info bar button tool-tip
-#: src/client/application/application-main-window.vala:574
+#: src/client/application/application-main-window.vala:594
msgid "Check the security details for the connection"
msgstr "Bağlantı güvenlik ayrıntılarını gözden geçirin"
#. / Translators: Main window title, first string
#. / substitution being the currently selected folder name,
#. / the second being the selected account name.
-#: src/client/application/application-main-window.vala:617
+#: src/client/application/application-main-window.vala:637
#, c-format
msgid "%s — %s"
msgstr "%s — %s"
@@ -1220,47 +1179,47 @@ msgstr "%s — %s"
#. Translators: The name of the folder group containing
#. folders created by people (as opposed to special-use
#. folders)
-#: src/client/application/application-main-window.vala:996
+#: src/client/application/application-main-window.vala:1019
#: src/client/folder-list/folder-list-account-branch.vala:43
msgid "Labels"
msgstr "Etiketler"
-#: src/client/application/application-main-window.vala:1293
+#: src/client/application/application-main-window.vala:1337
#, c-format
msgid "Empty all email from your %s folder?"
msgstr "%s klasörünüzdeki tüm e-postaları boşalt?"
-#: src/client/application/application-main-window.vala:1294
+#: src/client/application/application-main-window.vala:1338
msgid "This removes the email from Geary and your email server."
msgstr "Bu işlem e-postayı Gearyden ve e-posta sunucunuzdan kaldırır."
-#: src/client/application/application-main-window.vala:1295
+#: src/client/application/application-main-window.vala:1339
msgid "This cannot be undone."
msgstr "Bu geri alınamaz."
-#: src/client/application/application-main-window.vala:1296
+#: src/client/application/application-main-window.vala:1340
#, c-format
msgid "Empty %s"
msgstr "%s boşalt"
#. / Translators: Primary text for a confirmation dialog
-#: src/client/application/application-main-window.vala:1353
+#: src/client/application/application-main-window.vala:1397
msgid "Do you want to permanently delete this conversation?"
msgid_plural "Do you want to permanently delete these conversations?"
msgstr[0] "Bu konuşmaları kalıcı olarak silmek istiyor musunuz?"
-#: src/client/application/application-main-window.vala:1358
-#: src/client/application/application-main-window.vala:1373
+#: src/client/application/application-main-window.vala:1402
+#: src/client/application/application-main-window.vala:1417
msgid "Delete"
msgstr "Sil"
#. / Translators: Primary text for a confirmation dialog
-#: src/client/application/application-main-window.vala:1368
+#: src/client/application/application-main-window.vala:1412
msgid "Do you want to permanently delete this message?"
msgid_plural "Do you want to permanently delete these messages?"
msgstr[0] "Bu ileti(ler)i kalıcı olarak silmek istiyor musunuz?"
-#: src/client/application/application-main-window.vala:1691
+#: src/client/application/application-main-window.vala:1703
#, c-format
msgid "%s (%d)"
msgstr "%s (%d)"
@@ -1271,7 +1230,7 @@ msgstr "%s (%d)"
#. Document (100.9MB)
#. / In the composer, the filename followed by its filesize, i.e. "notes.txt (1.12KB)"
#: src/client/components/components-attachment-pane.vala:107
-#: src/client/composer/composer-widget.vala:1784
+#: src/client/composer/composer-widget.vala:1792
#, c-format
msgid "%s (%s)"
msgstr "%s (%s)"
@@ -1292,6 +1251,36 @@ msgstr ""
msgid "Dont _ask me again"
msgstr "Yeniden _sorma"
+#: src/client/components/components-conversation-actions.vala:90
+msgid "Mark conversation"
+msgid_plural "Mark conversations"
+msgstr[0] "Konuşmayı imle"
+
+#: src/client/components/components-conversation-actions.vala:95
+msgid "Add label to conversation"
+msgid_plural "Add label to conversations"
+msgstr[0] "Konuşmayı etiketle"
+
+#: src/client/components/components-conversation-actions.vala:100
+msgid "Move conversation"
+msgid_plural "Move conversations"
+msgstr[0] "Konuşmayı taşı"
+
+#: src/client/components/components-conversation-actions.vala:105
+msgid "Archive conversation"
+msgid_plural "Archive conversations"
+msgstr[0] "Konuşmayı arşivle"
+
+#: src/client/components/components-conversation-actions.vala:116
+msgid "Move conversation to Trash"
+msgid_plural "Move conversations to Trash"
+msgstr[0] "Konuşmayı Çöpʼe taşı"
+
+#: src/client/components/components-conversation-actions.vala:126
+msgid "Delete conversation"
+msgid_plural "Delete conversations"
+msgstr[0] "Konuşmayı sil"
+
#: src/client/components/components-inspector.vala:78
msgid "Inspector"
msgstr "İnceleyici"
@@ -1322,7 +1311,7 @@ msgstr "Farklı Kaydet"
#: src/client/components/components-inspector.vala:230
#: src/client/dialogs/dialogs-problem-details-dialog.vala:224
-#: ui/accounts_editor_servers_pane.ui:17
+#: ui/accounts_editor_servers_pane.ui:17 ui/composer-headerbar.ui:61
msgid "Cancel"
msgstr "İptal Et"
@@ -1338,15 +1327,10 @@ msgstr "Konuşma ön izlemesini _göster"
#. / Translators: Preferences label
#: src/client/components/components-preferences-window.vala:144
-msgid "Use _three pane view"
-msgstr "_Üç bölmeli görünümü kullan"
-
-#. / Translators: Preferences label
-#: src/client/components/components-preferences-window.vala:154
msgid "Use _single key email shortcuts"
msgstr "_Tek tuşlu eposta kısayolları kullan"
-#: src/client/components/components-preferences-window.vala:156
+#: src/client/components/components-preferences-window.vala:146
msgid ""
"Enable keyboard shortcuts for email actions that do not require pressing "
"<Ctrl>"
@@ -1355,22 +1339,22 @@ msgstr ""
"etkinleştir"
#. / Translators: Preferences label
-#: src/client/components/components-preferences-window.vala:167
+#: src/client/components/components-preferences-window.vala:157
msgid "_Watch for new mail when closed"
msgstr "Kapatıldığında yeni postayı _gözetle"
#. / Translators: Preferences tooltip
-#: src/client/components/components-preferences-window.vala:171
+#: src/client/components/components-preferences-window.vala:161
msgid "Geary will keep running after all windows are closed"
msgstr "Geary, tüm pencereler kapatıldıktan sonra çalışmayı sürdürecek"
#. / Translators: Preferences page title
-#: src/client/components/components-preferences-window.vala:189
+#: src/client/components/components-preferences-window.vala:178
msgid "Preferences"
msgstr "Tercihler"
#. / Translators: Preferences page title
-#: src/client/components/components-preferences-window.vala:250
+#: src/client/components/components-preferences-window.vala:234
msgid "Plugins"
msgstr "Eklentiler"
@@ -1489,36 +1473,6 @@ msgstr "Sunucu adı gerekli"
msgid "Could not look up server name"
msgstr "Sunucu adı yoklanamıyor"
-#: src/client/components/main-toolbar.vala:116
-msgid "Mark conversation"
-msgid_plural "Mark conversations"
-msgstr[0] "Konuşmayı imle"
-
-#: src/client/components/main-toolbar.vala:121
-msgid "Add label to conversation"
-msgid_plural "Add label to conversations"
-msgstr[0] "Konuşmayı etiketle"
-
-#: src/client/components/main-toolbar.vala:126
-msgid "Move conversation"
-msgid_plural "Move conversations"
-msgstr[0] "Konuşmayı taşı"
-
-#: src/client/components/main-toolbar.vala:131
-msgid "Archive conversation"
-msgid_plural "Archive conversations"
-msgstr[0] "Konuşmayı arşivle"
-
-#: src/client/components/main-toolbar.vala:142
-msgid "Move conversation to Trash"
-msgid_plural "Move conversations to Trash"
-msgstr[0] "Konuşmayı Çöpʼe taşı"
-
-#: src/client/components/main-toolbar.vala:152
-msgid "Delete conversation"
-msgid_plural "Delete conversations"
-msgstr[0] "Konuşmayı sil"
-
#. / Displayed in the space-limited status bar while a message is in the process of being sent.
#: src/client/components/status-bar.vala:26
msgid "Sending…"
@@ -1650,92 +1604,92 @@ msgstr ""
#. Translators: This dialog text is displayed to the
#. user when closing a composer where the options are
#. Keep, Discard or Cancel.
-#: src/client/composer/composer-widget.vala:862
+#: src/client/composer/composer-widget.vala:865
msgid "Do you want to keep or discard this draft message?"
msgstr "Bu iletiyi saklamak mı yoksa gözden çıkarmak mı istersiniz?"
#. Translators: This dialog text is displayed to the
#. user when closing a composer where the options are
#. only Discard or Cancel.
-#: src/client/composer/composer-widget.vala:888
+#: src/client/composer/composer-widget.vala:891
msgid "Do you want to discard this draft message?"
msgstr "Bu taslak iletiyi gözden çıkarmak istiyor musunuz?"
-#: src/client/composer/composer-widget.vala:1440
+#: src/client/composer/composer-widget.vala:1448
msgid "Send message with an empty subject and body?"
msgstr "İleti konusu ve gövdesi olmadan gönderilsin mi?"
-#: src/client/composer/composer-widget.vala:1442
+#: src/client/composer/composer-widget.vala:1450
msgid "Send message with an empty subject?"
msgstr "İleti konusu olmadan gönderilsin mi?"
-#: src/client/composer/composer-widget.vala:1444
+#: src/client/composer/composer-widget.vala:1452
msgid "Send message with an empty body?"
msgstr "İleti, ileti gövdesi olmadan gönderilsin mi?"
-#: src/client/composer/composer-widget.vala:1453
+#: src/client/composer/composer-widget.vala:1461
msgid "Send message without an attachment?"
msgstr "İleti eki olmadan gönderilsin mi?"
-#: src/client/composer/composer-widget.vala:1772
+#: src/client/composer/composer-widget.vala:1780
#, c-format
msgid "“%s” already attached for delivery."
msgstr "“%s” gönderim için zaten eklendi."
-#: src/client/composer/composer-widget.vala:1804
-#: src/client/composer/composer-widget.vala:1854
+#: src/client/composer/composer-widget.vala:1812
+#: src/client/composer/composer-widget.vala:1862
#, c-format
msgid "“%s” is an empty file."
msgstr "“%s” boş bir dosya."
-#: src/client/composer/composer-widget.vala:1842
+#: src/client/composer/composer-widget.vala:1850
#, c-format
msgid "“%s” could not be found."
msgstr "“%s” bulunamadı."
-#: src/client/composer/composer-widget.vala:1848
+#: src/client/composer/composer-widget.vala:1856
#, c-format
msgid "“%s” is a folder."
msgstr "“%s” bir klasör."
-#: src/client/composer/composer-widget.vala:1867
+#: src/client/composer/composer-widget.vala:1875
#, c-format
msgid "“%s” could not be opened for reading."
msgstr "“%s” okuma için açılamadı."
-#: src/client/composer/composer-widget.vala:1875
+#: src/client/composer/composer-widget.vala:1883
msgid "Cannot add attachment"
msgstr "Eklenti eklenemiyor"
#. Translators: This is the name of the file chooser filter
#. when inserting an image in the composer.
-#: src/client/composer/composer-widget.vala:1946
+#: src/client/composer/composer-widget.vala:1954
msgid "Images"
msgstr "Resimler"
#. Translators: Human-readable version of the RFC 822 To header
-#: src/client/composer/composer-widget.vala:2010
+#: src/client/composer/composer-widget.vala:2018
#: src/client/conversation-viewer/conversation-email.vala:542
#: src/client/util/util-email.vala:249 ui/conversation-message.ui:312
msgid "To:"
msgstr "Kime:"
#. Translators: Human-readable version of the RFC 822 CC header
-#: src/client/composer/composer-widget.vala:2016
+#: src/client/composer/composer-widget.vala:2024
#: src/client/conversation-viewer/conversation-email.vala:547
#: src/client/util/util-email.vala:254 ui/conversation-message.ui:357
msgid "Cc:"
msgstr "Cc:"
#. Translators: Human-readable version of the RFC 822 BCC header
-#: src/client/composer/composer-widget.vala:2022
+#: src/client/composer/composer-widget.vala:2030
#: src/client/conversation-viewer/conversation-email.vala:552
#: ui/conversation-message.ui:402
msgid "Bcc:"
msgstr "Bcc:"
#. Translators: Human-readable version of the RFC 822 Reply-To header
-#: src/client/composer/composer-widget.vala:2028
+#: src/client/composer/composer-widget.vala:2036
msgid "Reply-To: "
msgstr "Şuna Yanıtla: "
@@ -1744,7 +1698,7 @@ msgstr "Şuna Yanıtla: "
#. printf argument will be the alternate email address,
#. and the second will be the account's primary email
#. address.
-#: src/client/composer/composer-widget.vala:2146
+#: src/client/composer/composer-widget.vala:2154
#, c-format
msgid "%1$s via %2$s"
msgstr "%2$s aracılığıyla %1$s"
@@ -1762,49 +1716,49 @@ msgid "Search for more languages"
msgstr "Daha çok dil için ara"
#. / Translators: Context menu item
-#: src/client/conversation-list/conversation-list-view.vala:339
+#: src/client/conversation-list/conversation-list-view.vala:389
msgid "Move conversation to _Trash"
msgid_plural "Move conversations to _Trash"
msgstr[0] "Konuşmayı _Çöpʼe taşı"
#. / Translators: Context menu item
-#: src/client/conversation-list/conversation-list-view.vala:351
+#: src/client/conversation-list/conversation-list-view.vala:401
msgid "_Delete conversation"
msgid_plural "_Delete conversations"
msgstr[0] "Konuşmayı _sil"
-#: src/client/conversation-list/conversation-list-view.vala:364
+#: src/client/conversation-list/conversation-list-view.vala:414
#: ui/main-toolbar-menus.ui:5
msgid "Mark as _Read"
msgstr "_Okundu olarak imle"
-#: src/client/conversation-list/conversation-list-view.vala:372
+#: src/client/conversation-list/conversation-list-view.vala:422
#: ui/main-toolbar-menus.ui:9
msgid "Mark as _Unread"
msgstr "Ok_unmamış olarak imle"
-#: src/client/conversation-list/conversation-list-view.vala:380
+#: src/client/conversation-list/conversation-list-view.vala:430
#: ui/main-toolbar-menus.ui:17
msgid "U_nstar"
msgstr "Y_ıldızı kaldır"
-#: src/client/conversation-list/conversation-list-view.vala:387
+#: src/client/conversation-list/conversation-list-view.vala:437
#: ui/main-toolbar-menus.ui:13
msgid "_Star"
msgstr "_Yıldızla"
#. Translators: Menu item to reply to a specific message.
-#: src/client/conversation-list/conversation-list-view.vala:396
+#: src/client/conversation-list/conversation-list-view.vala:446
#: ui/conversation-email-menus.ui:9
msgid "_Reply"
msgstr "_Yanıtla"
-#: src/client/conversation-list/conversation-list-view.vala:402
+#: src/client/conversation-list/conversation-list-view.vala:452
msgid "R_eply All"
msgstr "Tümüne _Yanıtla"
#. Translators: Menu item to forward a specific message.
-#: src/client/conversation-list/conversation-list-view.vala:408
+#: src/client/conversation-list/conversation-list-view.vala:458
#: ui/conversation-email-menus.ui:21
msgid "_Forward"
msgstr "_Yönlendir"
@@ -1894,25 +1848,25 @@ msgstr "Gönderenden her zaman göster"
#. Translators: Title label for placeholder when no
#. conversations have been selected.
-#: src/client/conversation-viewer/conversation-viewer.vala:83
+#: src/client/conversation-viewer/conversation-viewer.vala:87
msgid "No conversations selected"
msgstr "Konuşma seçilmedi"
#. Translators: Sub-title label for placeholder when no
#. conversations have been selected.
-#: src/client/conversation-viewer/conversation-viewer.vala:87
+#: src/client/conversation-viewer/conversation-viewer.vala:91
msgid "Selecting a conversation from the list will display it here"
msgstr "Listeden bir ileti seçtiğinizde burada gösterilecek"
#. Translators: Title label for placeholder when multiple
#. conversations have been selected.
-#: src/client/conversation-viewer/conversation-viewer.vala:96
+#: src/client/conversation-viewer/conversation-viewer.vala:100
msgid "Multiple conversations selected"
msgstr "Birden çok konuşma seçildi"
#. Translators: Sub-title label for placeholder when multiple
#. conversations have been selected.
-#: src/client/conversation-viewer/conversation-viewer.vala:100
+#: src/client/conversation-viewer/conversation-viewer.vala:104
msgid "Choosing an action will apply to all selected conversations"
msgstr "Bir eylem seçtiğinizde tüm seçili konuşmalara uygulanacaktır"
@@ -1920,20 +1874,20 @@ msgstr "Bir eylem seçtiğinizde tüm seçili konuşmalara uygulanacaktır"
#. conversations have exist in a folder.
#. Translators: Title label for placeholder when no
#. conversations have been found in a search.
-#: src/client/conversation-viewer/conversation-viewer.vala:109
-#: src/client/conversation-viewer/conversation-viewer.vala:122
+#: src/client/conversation-viewer/conversation-viewer.vala:113
+#: src/client/conversation-viewer/conversation-viewer.vala:126
msgid "No conversations found"
msgstr "Konuşma bulunamadı"
#. Translators: Sub-title label for placeholder when no
#. conversations have exist in a folder.
-#: src/client/conversation-viewer/conversation-viewer.vala:113
+#: src/client/conversation-viewer/conversation-viewer.vala:117
msgid "This folder does not contain any conversations"
msgstr "Bu klasör herhangi bir konuşma içermiyor"
#. Translators: Sub-title label for placeholder when no
#. conversations have been found in a search.
-#: src/client/conversation-viewer/conversation-viewer.vala:126
+#: src/client/conversation-viewer/conversation-viewer.vala:130
msgid "Your search returned no results, try refining your search terms"
msgstr "Aramanız sonuçsuz kaldı, arama terimlerinizi arıtmayı deneyin"
@@ -2652,7 +2606,7 @@ msgstr "okunmadı"
#. Draft mailbox. Separate names using a vertical bar and
#. put the most common localized name to the front for the
#. default. English names do not need to be included.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:996
+#: src/engine/imap-engine/imap-engine-generic-account.vala:998
msgid "Drafts | Draft"
msgstr "Taslaklar | Taslak"
@@ -2660,14 +2614,14 @@ msgstr "Taslaklar | Taslak"
#. Sent mailbox. Separate names using a vertical bar and
#. put the most common localized name to the front for the
#. default. English names do not need to be included.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1005
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1007
msgid "Sent | Sent Mail | Sent Email | Sent E-Mail"
msgstr ""
"Gönderilmiş | Gönderilmiş Posta | Gönderilmiş Eposta | Gönderilmiş E-Posta"
#. The localised name(s) of the Sent folder name as used
#. by MS Outlook/Exchange.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1010
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1012
msgctxt "Outlook localised name"
msgid "Sent Items"
msgstr "Gönderilmiş Ögeler"
@@ -2676,7 +2630,7 @@ msgstr "Gönderilmiş Ögeler"
#. Junk/Spam mailbox. Separate names using a vertical bar
#. and put the most common localized name to the front for
#. the default. English names do not need to be included.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1020
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1022
msgid ""
"Junk | Spam | Junk Mail | Junk Email | Junk E-Mail | Bulk Mail | Bulk Email "
"| Bulk E-Mail"
@@ -2688,13 +2642,13 @@ msgstr ""
#. Trash mailbox. Separate names using a vertical bar and
#. put the most common localized name to the front for the
#. default. English names do not need to be included.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1030
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1032
msgid "Trash | Rubbish | Rubbish Bin"
msgstr "Çöp | Çöp | Çöp Kutusu"
#. The localised name(s) of the Trash folder name as used
#. by MS Outlook/Exchange.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1035
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1037
msgctxt "Outlook localised name"
msgid "Deleted Items"
msgstr "Silinen Ögeler"
@@ -2703,7 +2657,7 @@ msgstr "Silinen Ögeler"
#. Archive mailbox. Separate names using a vertical bar
#. and put the most common localized name to the front for
#. the default. English names do not need to be included.
-#: src/engine/imap-engine/imap-engine-generic-account.vala:1045
+#: src/engine/imap-engine/imap-engine-generic-account.vala:1047
msgid "Archive | Archives"
msgstr "Arşiv | Arşivler"
@@ -3079,6 +3033,26 @@ msgstr "Seçilen ekleri aç"
msgid "Save _All"
msgstr "_Tümünü Kaydet"
+#: ui/components-conversation-actions.ui:85
+msgid "Reply"
+msgstr "Yanıtla"
+
+#: ui/components-conversation-actions.ui:108
+msgid "Reply All"
+msgstr "Tümüne Yanıtla"
+
+#: ui/components-conversation-actions.ui:131
+msgid "Forward"
+msgstr "Yönlendir"
+
+#: ui/components-conversation-actions.ui:163
+msgid "_Archive"
+msgstr "_Arşivle"
+
+#: ui/components-conversation-actions.ui:211
+msgid "Toggle find bar"
+msgstr "Bulma çubuğunu aç"
+
#: ui/components-inspector-error-view.ui:31
msgid ""
"If the problem is serious or persists, please save and send these details to "
@@ -3616,35 +3590,19 @@ msgctxt "shortcut window"
msgid "Insert a link"
msgstr "Bağlantı yerleştir"
-#: ui/main-toolbar.ui:24
+#: ui/main-toolbar.ui:85 ui/main-toolbar.ui:179
+msgid "Back"
+msgstr "Geri"
+
+#: ui/main-toolbar.ui:104
msgctxt "tooltip"
msgid "Compose Message"
msgstr "İleti Oluştur"
-#: ui/main-toolbar.ui:62
+#: ui/main-toolbar.ui:122
msgid "Toggle search bar"
msgstr "Arama çubuğunu aç"
-#: ui/main-toolbar.ui:114
-msgid "Reply"
-msgstr "Yanıtla"
-
-#: ui/main-toolbar.ui:137
-msgid "Reply All"
-msgstr "Tümüne Yanıtla"
-
-#: ui/main-toolbar.ui:160
-msgid "Forward"
-msgstr "Yönlendir"
-
-#: ui/main-toolbar.ui:265
-msgid "Toggle find bar"
-msgstr "Bulma çubuğunu aç"
-
-#: ui/main-toolbar.ui:286
-msgid "_Archive"
-msgstr "_Arşivle"
-
#: ui/main-toolbar-menus.ui:21
msgid "Toggle as _Junk"
msgstr "_Gereksiz olarak imle"
@@ -3681,6 +3639,41 @@ msgstr "_Kimlik Doğrula"
msgid "Geary update in progress…"
msgstr "Geary güncellemesi sürüyor…"
+#~ msgid "Position of folder list pane"
+#~ msgstr "Klasör listesi bölmesinin konumu"
+
+#~ msgid "Position of the folder list Paned grabber."
+#~ msgstr "Klasör listesi bölmesi yakalayıcının konumu."
+
+#~ msgid "Position of folder list pane when horizontal"
+#~ msgstr "Klasör listesi bölmesinin yataykenki konumu"
+
+#~ msgid ""
+#~ "Position of the folder list Paned grabber in the horizontal orientation."
+#~ msgstr "Yatay yönelimde klasör listesi bölmesi yakalayıcının konumu."
+
+#~ msgid "Position of folder list pane when vertical"
+#~ msgstr "Klasör listesi bölmesinin dikeykenki konumu"
+
+#~ msgid ""
+#~ "Position of the folder list Paned grabber in the vertical orientation."
+#~ msgstr "Dikey yönelimde klasör listesi bölmesi yakalayıcının konumu."
+
+#~ msgid "Orientation of the folder list pane"
+#~ msgstr "Klasör listesi bölmesinin konumlandırması"
+
+#~ msgid "True if the folder list Paned is in the horizontal orientation."
+#~ msgstr "Eğer klasör listesi bölmesi yatay yönelimdeyse doğru."
+
+#~ msgid "Position of message list pane"
+#~ msgstr "İleti listesi bölmesinin konumu"
+
+#~ msgid "Position of the message list Paned grabber."
+#~ msgstr "İleti listesi bölmesi yakalayıcının konumu."
+
+#~ msgid "Use _three pane view"
+#~ msgstr "_Üç bölmeli görünümü kullan"
+
#~ msgid "Desktop Notifications"
#~ msgstr "Masaüstü Bildirimleri"
--
2.29.2

Some files were not shown because too many files have changed in this diff Show More