diff --git a/CMakeLists.txt b/CMakeLists.txt
index cea4a790..531306d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,7 +26,7 @@ set(ARCHIVE_DEBUILD_FULL_NAME ${CMAKE_PROJECT_NAME}_${VERSION}.orig.tar.xz)
set(GLADE_FILES ui/account_list.glade ui/account_cannot_remove.glade ui/account_spinner.glade
ui/composer.glade ui/login.glade ui/message.glade ui/password-dialog.glade ui/preferences.glade
- ui/remove_confirm.glade ui/toolbar.glade)
+ ui/remove_confirm.glade)
option(DEBUG "Build for debugging." OFF)
option(ICON_UPDATE "Run gtk-update-icon-cache after the install." ON)
diff --git a/icons/16x16/format-text-none.svg b/icons/16x16/format-text-none.svg
deleted file mode 100644
index f864f351..00000000
--- a/icons/16x16/format-text-none.svg
+++ /dev/null
@@ -1,313 +0,0 @@
-
-
-
-
diff --git a/icons/16x16/mail-archive.svg b/icons/16x16/mail-archive.svg
deleted file mode 100644
index 79a9661b..00000000
--- a/icons/16x16/mail-archive.svg
+++ /dev/null
@@ -1,350 +0,0 @@
-
-
-
-
diff --git a/icons/16x16/tag-new.svg b/icons/16x16/tag-new.svg
deleted file mode 100644
index f8adec63..00000000
--- a/icons/16x16/tag-new.svg
+++ /dev/null
@@ -1,240 +0,0 @@
-
-
-
-
diff --git a/icons/16x16/tag.svg b/icons/16x16/tag.svg
deleted file mode 100644
index b845b319..00000000
--- a/icons/16x16/tag.svg
+++ /dev/null
@@ -1,230 +0,0 @@
-
-
-
-
diff --git a/icons/16x16/tags.svg b/icons/16x16/tags.svg
deleted file mode 100644
index be9a0b13..00000000
--- a/icons/16x16/tags.svg
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/edit-mark.svg b/icons/24x24/edit-mark.svg
deleted file mode 100644
index af8b3c26..00000000
--- a/icons/24x24/edit-mark.svg
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/format-text-none.svg b/icons/24x24/format-text-none.svg
deleted file mode 100644
index 2757e6dc..00000000
--- a/icons/24x24/format-text-none.svg
+++ /dev/null
@@ -1,452 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/mail-archive.svg b/icons/24x24/mail-archive.svg
deleted file mode 100644
index 8308455b..00000000
--- a/icons/24x24/mail-archive.svg
+++ /dev/null
@@ -1,446 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/mail-move.svg b/icons/24x24/mail-move.svg
deleted file mode 100644
index 9a055ea1..00000000
--- a/icons/24x24/mail-move.svg
+++ /dev/null
@@ -1,360 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/tag-new.svg b/icons/24x24/tag-new.svg
deleted file mode 100644
index d43c2d37..00000000
--- a/icons/24x24/tag-new.svg
+++ /dev/null
@@ -1,215 +0,0 @@
-
-
-
-
diff --git a/icons/24x24/tag.svg b/icons/24x24/tag.svg
deleted file mode 100644
index 76326711..00000000
--- a/icons/24x24/tag.svg
+++ /dev/null
@@ -1,338 +0,0 @@
-
-
-
-
diff --git a/icons/CMakeLists.txt b/icons/CMakeLists.txt
index f9b3442f..1a86ac08 100644
--- a/icons/CMakeLists.txt
+++ b/icons/CMakeLists.txt
@@ -1,37 +1,28 @@
set(ICONS_DEST share/geary/icons)
set(ICON_FILES
- application-menu.svg
+ archive-symbolic.svg
+ close-symbolic.svg
+ document-symbolic.svg
edit-symbolic.svg
- mail-inbox.png
- mail-outbox.svg
- mail-sent.png
- non-starred-grey.png
- starred.png
-)
-
-set(ICON_FILES_16
- 16x16/format-text-none.svg
- 16x16/mail-archive.svg
- 16x16/tag.svg
- 16x16/tag-new.svg
- 16x16/tags.svg
-)
-
-set(ICON_FILES_24
- 24x24/format-text-none.svg
- 24x24/edit-mark.svg
- 24x24/mail-archive.svg
- 24x24/mail-move.svg
- 24x24/tag.svg
- 24x24/tag-new.svg
+ format-text-remove-symbolic.svg
+ forward-symbolic.svg
+ inbox-symbolic.svg
+ marker-symbolic.svg
+ outbox-symbolic.svg
+ read-symbolic.svg
+ reply-all-symbolic.svg
+ reply-symbolic.svg
+ sent-symbolic.svg
+ spam-symbolic.svg
+ star-symbolic.svg
+ tag-symbolic.svg
+ unread-symbolic.svg
+ unstarred-symbolic.svg
)
install(FILES ${ICON_FILES} DESTINATION ${ICONS_DEST})
-install(FILES ${ICON_FILES_16} DESTINATION ${ICONS_DEST}/16x16)
-install(FILES ${ICON_FILES_24} DESTINATION ${ICONS_DEST}/24x24)
-
# Application icon goes in theme directory
install(FILES "16x16/geary.svg" DESTINATION share/icons/hicolor/16x16/apps)
install(FILES "32x32/geary.svg" DESTINATION share/icons/hicolor/32x32/apps)
diff --git a/icons/COPYING.icons b/icons/COPYING.icons
new file mode 100644
index 00000000..ff3b4996
--- /dev/null
+++ b/icons/COPYING.icons
@@ -0,0 +1,51 @@
+The following works are licensed under a Creative Commons Attribution 3.0 Unported License.
+For more information, see: http://creativecommons.org/licenses/by/3.0/
+
+star-symbolic.svg
+unstarred-symbolic.svg
+ Star designed by SuperAtic LABS from The Noun Project
+ http://thenounproject.com/noun/star/#icon-No15362
+ http://thenounproject.com/noun/star/#icon-No15361
+
+archive-symbolic.svg
+ Inbox designed by Erik from The Noun Project
+ http://thenounproject.com/noun/inbox/#icon-No17098
+
+reply-symbolic.svg
+reply-all-symbolic.svg (derivative)
+forward-symbolic.svg (derivative)
+ Back designed by Anisha Varghese from The Noun Project
+ http://thenounproject.com/noun/back/#icon-No14792
+
+marker-symbolic.svg
+ Marker designed by factor[e] design initiative from The Noun Project
+ http://thenounproject.com/noun/marker/#icon-No18908
+
+inbox-symbolic.svg
+outbox-symbolic.svg (derivative)
+ Inbox designed by Edward Boatman from The Noun Project
+ http://thenounproject.com/noun/inbox/#icon-No406
+
+sent-symbolic.svg
+ Paper Airplane designed by George Vasjagin from The Noun Project
+ http://thenounproject.com/noun/paper-airplane/#icon-No12136
+
+document-symbolic.svg
+ Document designed by Edward Boatman from The Noun Project
+ http://thenounproject.com/noun/document/#icon-No453
+
+spam-symbolic.svg
+ Pig designed by aLf from The Noun Project
+ http://thenounproject.com/noun/pig/#icon-No5271
+
+close-symbolic.svg
+ Close designed by Icomatic from The Noun Project
+ http://thenounproject.com/noun/close/#icon-No18932
+
+
+The following work is public domain.
+
+tag-symbolic.svg
+ Tag designed by Ian Hamilton, 2012
+ http://thenounproject.com/noun/tag/#icon-No4018
+
diff --git a/icons/application-menu.svg b/icons/application-menu.svg
deleted file mode 100644
index 072b3a45..00000000
--- a/icons/application-menu.svg
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
diff --git a/icons/archive-symbolic.svg b/icons/archive-symbolic.svg
new file mode 100755
index 00000000..2209585b
--- /dev/null
+++ b/icons/archive-symbolic.svg
@@ -0,0 +1,60 @@
+
+
\ No newline at end of file
diff --git a/icons/close-symbolic.svg b/icons/close-symbolic.svg
new file mode 100755
index 00000000..1971e818
--- /dev/null
+++ b/icons/close-symbolic.svg
@@ -0,0 +1,52 @@
+
+
\ No newline at end of file
diff --git a/icons/document-symbolic.svg b/icons/document-symbolic.svg
new file mode 100755
index 00000000..434ad092
--- /dev/null
+++ b/icons/document-symbolic.svg
@@ -0,0 +1,48 @@
+
+
+
+
\ No newline at end of file
diff --git a/icons/format-text-remove-symbolic.svg b/icons/format-text-remove-symbolic.svg
new file mode 100644
index 00000000..678107a9
--- /dev/null
+++ b/icons/format-text-remove-symbolic.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/icons/forward-symbolic.svg b/icons/forward-symbolic.svg
new file mode 100644
index 00000000..ce5e3e69
--- /dev/null
+++ b/icons/forward-symbolic.svg
@@ -0,0 +1,47 @@
+
+
\ No newline at end of file
diff --git a/icons/inbox-symbolic.svg b/icons/inbox-symbolic.svg
new file mode 100644
index 00000000..cc54d484
--- /dev/null
+++ b/icons/inbox-symbolic.svg
@@ -0,0 +1,57 @@
+
+
+
+
\ No newline at end of file
diff --git a/icons/mail-inbox.png b/icons/mail-inbox.png
deleted file mode 100644
index 65c6973e..00000000
Binary files a/icons/mail-inbox.png and /dev/null differ
diff --git a/icons/mail-outbox.svg b/icons/mail-outbox.svg
deleted file mode 100644
index 641f4fa4..00000000
--- a/icons/mail-outbox.svg
+++ /dev/null
@@ -1,315 +0,0 @@
-
-
-
-
diff --git a/icons/mail-sent.png b/icons/mail-sent.png
deleted file mode 100644
index 5a26a638..00000000
Binary files a/icons/mail-sent.png and /dev/null differ
diff --git a/icons/marker-symbolic.svg b/icons/marker-symbolic.svg
new file mode 100644
index 00000000..6a1768ac
--- /dev/null
+++ b/icons/marker-symbolic.svg
@@ -0,0 +1,48 @@
+
+
+
+
\ No newline at end of file
diff --git a/icons/non-starred-grey.png b/icons/non-starred-grey.png
deleted file mode 100644
index 11485d71..00000000
Binary files a/icons/non-starred-grey.png and /dev/null differ
diff --git a/icons/outbox-symbolic.svg b/icons/outbox-symbolic.svg
new file mode 100644
index 00000000..abe93c47
--- /dev/null
+++ b/icons/outbox-symbolic.svg
@@ -0,0 +1,52 @@
+
+
+
+
\ No newline at end of file
diff --git a/icons/read-symbolic.svg b/icons/read-symbolic.svg
new file mode 100644
index 00000000..15111455
--- /dev/null
+++ b/icons/read-symbolic.svg
@@ -0,0 +1,66 @@
+
+
+
+
diff --git a/icons/reply-all-symbolic.svg b/icons/reply-all-symbolic.svg
new file mode 100644
index 00000000..18787104
--- /dev/null
+++ b/icons/reply-all-symbolic.svg
@@ -0,0 +1,53 @@
+
+
\ No newline at end of file
diff --git a/icons/reply-symbolic.svg b/icons/reply-symbolic.svg
new file mode 100755
index 00000000..9193d70c
--- /dev/null
+++ b/icons/reply-symbolic.svg
@@ -0,0 +1,46 @@
+
+
\ No newline at end of file
diff --git a/icons/sent-symbolic.svg b/icons/sent-symbolic.svg
new file mode 100755
index 00000000..24fe6f3c
--- /dev/null
+++ b/icons/sent-symbolic.svg
@@ -0,0 +1,53 @@
+
+
\ No newline at end of file
diff --git a/icons/spam-symbolic.svg b/icons/spam-symbolic.svg
new file mode 100755
index 00000000..bbe0e875
--- /dev/null
+++ b/icons/spam-symbolic.svg
@@ -0,0 +1,52 @@
+
+
\ No newline at end of file
diff --git a/icons/star-symbolic.svg b/icons/star-symbolic.svg
new file mode 100755
index 00000000..9a5c73d9
--- /dev/null
+++ b/icons/star-symbolic.svg
@@ -0,0 +1,46 @@
+
+
\ No newline at end of file
diff --git a/icons/starred.png b/icons/starred.png
deleted file mode 100644
index f58a3088..00000000
Binary files a/icons/starred.png and /dev/null differ
diff --git a/icons/tag-symbolic.svg b/icons/tag-symbolic.svg
new file mode 100755
index 00000000..c4faefbf
--- /dev/null
+++ b/icons/tag-symbolic.svg
@@ -0,0 +1,55 @@
+
+
\ No newline at end of file
diff --git a/icons/unread-symbolic.svg b/icons/unread-symbolic.svg
new file mode 100644
index 00000000..0604f51c
--- /dev/null
+++ b/icons/unread-symbolic.svg
@@ -0,0 +1,71 @@
+
+
+
+
diff --git a/icons/unstarred-symbolic.svg b/icons/unstarred-symbolic.svg
new file mode 100755
index 00000000..f95b18b3
--- /dev/null
+++ b/icons/unstarred-symbolic.svg
@@ -0,0 +1,46 @@
+
+
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8365fbd4..5202b996 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -300,6 +300,7 @@ client/accounts/account-spinner-page.vala
client/accounts/add-edit-page.vala
client/accounts/login-dialog.vala
+client/composer/composer-toolbar.vala
client/composer/composer-window.vala
client/composer/contact-entry-completion.vala
client/composer/contact-list-store.vala
@@ -342,6 +343,7 @@ client/ui/main-toolbar.vala
client/ui/main-window.vala
client/ui/monitored-progress-bar.vala
client/ui/monitored-spinner.vala
+client/ui/pill-toolbar.vala
client/ui/stock.vala
client/util/util-date.vala
diff --git a/src/client/composer/composer-toolbar.vala b/src/client/composer/composer-toolbar.vala
new file mode 100644
index 00000000..543621e0
--- /dev/null
+++ b/src/client/composer/composer-toolbar.vala
@@ -0,0 +1,46 @@
+/* Copyright 2011-2013 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class ComposerToolbar : PillToolbar {
+ public ComposerToolbar(Gtk.ActionGroup toolbar_action_group, Gtk.Menu menu) {
+ base(toolbar_action_group);
+
+ Gee.List insert = new Gee.ArrayList();
+
+ // Font formatting.
+ insert.add(create_toggle_button(null, ComposerWindow.ACTION_BOLD));
+ insert.add(create_toggle_button(null, ComposerWindow.ACTION_ITALIC));
+ insert.add(create_toggle_button(null, ComposerWindow.ACTION_UNDERLINE));
+ insert.add(create_toggle_button(null, ComposerWindow.ACTION_STRIKETHROUGH));
+ Gtk.ToolItem font_format_item = create_pill_buttons(insert, false, true);
+ add(font_format_item);
+
+ // Indent level.
+ insert.clear();
+ insert.add(create_toolbar_button(null, ComposerWindow.ACTION_INDENT));
+ insert.add(create_toolbar_button(null, ComposerWindow.ACTION_OUTDENT));
+ add(create_pill_buttons(insert, false));
+
+ // Link.
+ insert.clear();
+ insert.add(create_toolbar_button(null, ComposerWindow.ACTION_INSERT_LINK));
+ add(create_pill_buttons(insert));
+
+ // Remove formatting.
+ insert.clear();
+ insert.add(create_toolbar_button(null, ComposerWindow.ACTION_REMOVE_FORMAT));
+ add(create_pill_buttons(insert));
+
+ // Spacer.
+ add(create_spacer());
+
+ // Menu.
+ insert.clear();
+ insert.add(create_menu_button(null, menu, ComposerWindow.ACTION_MENU));
+ add(create_pill_buttons(insert));
+ }
+}
+
diff --git a/src/client/composer/composer-window.vala b/src/client/composer/composer-window.vala
index 2d72c038..b2ea7ae8 100644
--- a/src/client/composer/composer-window.vala
+++ b/src/client/composer/composer-window.vala
@@ -13,35 +13,35 @@ public class ComposerWindow : Gtk.Window {
FORWARD
}
+ public const string ACTION_UNDO = "undo";
+ public const string ACTION_REDO = "redo";
+ public const string ACTION_CUT = "cut";
+ public const string ACTION_COPY = "copy";
+ public const string ACTION_COPY_LINK = "copy link";
+ public const string ACTION_PASTE = "paste";
+ public const string ACTION_PASTE_FORMAT = "paste with formatting";
+ public const string ACTION_BOLD = "bold";
+ public const string ACTION_ITALIC = "italic";
+ public const string ACTION_UNDERLINE = "underline";
+ public const string ACTION_STRIKETHROUGH = "strikethrough";
+ public const string ACTION_REMOVE_FORMAT = "removeformat";
+ public const string ACTION_INDENT = "indent";
+ public const string ACTION_OUTDENT = "outdent";
+ public const string ACTION_JUSTIFY_LEFT = "justifyleft";
+ public const string ACTION_JUSTIFY_RIGHT = "justifyright";
+ public const string ACTION_JUSTIFY_CENTER = "justifycenter";
+ public const string ACTION_JUSTIFY_FULL = "justifyfull";
+ public const string ACTION_MENU = "menu";
+ public const string ACTION_COLOR = "color";
+ public const string ACTION_INSERT_LINK = "insertlink";
+ public const string ACTION_COMPOSE_AS_HTML = "compose as html";
+ public const string ACTION_CLOSE = "close";
+
private const string DEFAULT_TITLE = _("New Message");
private const string DRAFT_SAVED_TEXT = _("Saved");
private const string DRAFT_SAVING_TEXT = _("Saving draft...");
private const string DRAFT_ERROR_TEXT = _("Error saving draft");
- private const string ACTION_UNDO = "undo";
- private const string ACTION_REDO = "redo";
- private const string ACTION_CUT = "cut";
- private const string ACTION_COPY = "copy";
- private const string ACTION_COPY_LINK = "copy link";
- private const string ACTION_PASTE = "paste";
- private const string ACTION_PASTE_FORMAT = "paste with formatting";
- private const string ACTION_BOLD = "bold";
- private const string ACTION_ITALIC = "italic";
- private const string ACTION_UNDERLINE = "underline";
- private const string ACTION_STRIKETHROUGH = "strikethrough";
- private const string ACTION_REMOVE_FORMAT = "removeformat";
- private const string ACTION_INDENT = "indent";
- private const string ACTION_OUTDENT = "outdent";
- private const string ACTION_JUSTIFY_LEFT = "justifyleft";
- private const string ACTION_JUSTIFY_RIGHT = "justifyright";
- private const string ACTION_JUSTIFY_CENTER = "justifycenter";
- private const string ACTION_JUSTIFY_FULL = "justifyfull";
- private const string ACTION_MENU = "menu";
- private const string ACTION_COLOR = "color";
- private const string ACTION_INSERT_LINK = "insertlink";
- private const string ACTION_COMPOSE_AS_HTML = "compose as html";
- private const string ACTION_CLOSE = "close";
-
private const string URI_LIST_MIME_TYPE = "text/uri-list";
private const string FILE_URI_PREFIX = "file://";
private const string BODY_ID = "message-body";
@@ -150,7 +150,6 @@ public class ComposerWindow : Gtk.Window {
private Gtk.Entry subject_entry;
private Gtk.Button close_button;
private Gtk.Button send_button;
- private Gtk.ToggleToolButton menu_button;
private Gtk.Label message_overlay_label;
private WebKit.DOM.Element? prev_selected_link = null;
private Gtk.Box attachments_box;
@@ -162,14 +161,16 @@ public class ComposerWindow : Gtk.Window {
private Gtk.Widget visible_on_attachment_drag_over_child;
private Gtk.Label draft_save_label;
- private Gtk.Menu menu_html;
- private Gtk.Menu menu_plain;
+ private Gtk.Menu menu = new Gtk.Menu();
private Gtk.RadioMenuItem font_small;
private Gtk.RadioMenuItem font_medium;
private Gtk.RadioMenuItem font_large;
private Gtk.RadioMenuItem font_sans;
private Gtk.RadioMenuItem font_serif;
private Gtk.RadioMenuItem font_monospace;
+ private Gtk.MenuItem color_item;
+ private Gtk.MenuItem html_item;
+ private Gtk.MenuItem html_item2;
private Gtk.ActionGroup actions;
private string? hover_url = null;
@@ -260,7 +261,9 @@ public class ComposerWindow : Gtk.Window {
cc_entry.changed.connect(validate_send_button);
bcc_entry.changed.connect(validate_send_button);
- Gtk.Toolbar compose_toolbar = (Gtk.Toolbar) builder.get_object("compose_toolbar");
+ ComposerToolbar composer_toolbar = new ComposerToolbar(actions, menu);
+ Gtk.Alignment toolbar_area = (Gtk.Alignment) builder.get_object("toolbar area");
+ toolbar_area.add(composer_toolbar);
actions.get_action(ACTION_UNDO).activate.connect(on_action);
actions.get_action(ACTION_REDO).activate.connect(on_action);
@@ -287,7 +290,6 @@ public class ComposerWindow : Gtk.Window {
actions.get_action(ACTION_JUSTIFY_CENTER).activate.connect(on_formatting_action);
actions.get_action(ACTION_JUSTIFY_FULL).activate.connect(on_formatting_action);
- actions.get_action(ACTION_MENU).activate.connect(on_open_menu);
actions.get_action(ACTION_COLOR).activate.connect(on_select_color);
actions.get_action(ACTION_INSERT_LINK).activate.connect(on_insert_link);
@@ -383,52 +385,35 @@ public class ComposerWindow : Gtk.Window {
GearyApplication.instance.config.spell_check_changed.connect(on_spell_check_changed);
- menu_button = builder.get_object("menu button") as Gtk.ToggleToolButton;
-
- // Build menu
- menu_html = new Gtk.Menu();
- menu_html.deactivate.connect(on_deactivate_menu);
- menu_html.attach_to_widget(menu_button, null);
-
- font_sans = new Gtk.RadioMenuItem.with_label(new SList(),
- _("Sans Serif"));
+ // Font family menu items.
+ font_sans = new Gtk.RadioMenuItem(new SList());
font_sans.activate.connect(on_font_sans);
- menu_html.append(font_sans);
- font_serif = new Gtk.RadioMenuItem.with_label_from_widget(font_sans, _("Serif"));
+ font_sans.related_action = ui.get_action("ui/font_sans");
+ font_serif = new Gtk.RadioMenuItem.from_widget(font_sans);
font_serif.activate.connect(on_font_serif);
- menu_html.append(font_serif);
- font_monospace = new Gtk.RadioMenuItem.with_label_from_widget(font_sans,
- _("Fixed width"));
+ font_serif.related_action = ui.get_action("ui/font_serif");
+ font_monospace = new Gtk.RadioMenuItem.from_widget(font_sans);
+ font_monospace.related_action = ui.get_action("ui/font_monospace");
font_monospace.activate.connect(on_font_monospace);
- menu_html.append(font_monospace);
- menu_html.append(new Gtk.SeparatorMenuItem());
- font_small = new Gtk.RadioMenuItem.with_label(new SList(), _("Small"));
+ // Font size menu items.
+ font_small = new Gtk.RadioMenuItem(new SList());
+ font_small.related_action = ui.get_action("ui/font_small");
font_small.activate.connect(on_font_size_small);
- menu_html.append(font_small);
- font_medium = new Gtk.RadioMenuItem.with_label_from_widget(font_small, _("Medium"));
+ font_medium = new Gtk.RadioMenuItem.from_widget(font_small);
+ font_medium.related_action = ui.get_action("ui/font_medium");
font_medium.activate.connect(on_font_size_medium);
- menu_html.append(font_medium);
- font_large = new Gtk.RadioMenuItem.with_label_from_widget(font_small, _("Large"));
+ font_large = new Gtk.RadioMenuItem.from_widget(font_small);
+ font_large.related_action = ui.get_action("ui/font_large");
font_large.activate.connect(on_font_size_large);
- menu_html.append(font_large);
- menu_html.append(new Gtk.SeparatorMenuItem());
- Gtk.MenuItem color_item = new Gtk.MenuItem();
+ color_item = new Gtk.MenuItem();
color_item.related_action = ui.get_action("ui/color");
- menu_html.append(color_item);
- menu_html.append(new Gtk.SeparatorMenuItem());
- Gtk.MenuItem html_item = new Gtk.CheckMenuItem();
+ html_item = new Gtk.CheckMenuItem();
html_item.related_action = ui.get_action("ui/htmlcompose");
- menu_html.append(html_item);
- menu_plain = new Gtk.Menu();
- menu_plain.deactivate.connect(on_deactivate_menu);
- menu_plain.attach_to_widget(menu_button, null);
-
- Gtk.MenuItem html_item2 = new Gtk.CheckMenuItem();
+ html_item2 = new Gtk.CheckMenuItem();
html_item2.related_action = ui.get_action("ui/htmlcompose");
- menu_plain.append(html_item2);
WebKit.WebSettings s = new WebKit.WebSettings();
s.enable_spell_checking = GearyApplication.instance.config.spell_check;
@@ -451,7 +436,7 @@ public class ComposerWindow : Gtk.Window {
List chain = new List();
chain.append(hidden_on_attachment_drag_over);
chain.append(message_area);
- chain.append(compose_toolbar);
+ chain.append(composer_toolbar);
chain.append(attachments_box);
chain.append(button_area);
box.set_focus_chain(chain);
@@ -855,7 +840,7 @@ public class ComposerWindow : Gtk.Window {
a.sensitive = false;
// Disable buttons.
- close_button.sensitive = send_button.sensitive = menu_button.sensitive =
+ close_button.sensitive = send_button.sensitive =
add_attachment_button.sensitive = pending_attachments_button.sensitive = false;
// Disable editable widgets.
@@ -1166,6 +1151,7 @@ public class ComposerWindow : Gtk.Window {
WebKit.DOM.DOMTokenList body_classes = editor.get_dom_document().body.get_class_list();
if (!compose_as_html) {
toggle_toolbar_buttons(false);
+ build_plaintext_menu();
try {
body_classes.add("plain");
} catch (Error error) {
@@ -1173,6 +1159,7 @@ public class ComposerWindow : Gtk.Window {
}
} else {
toggle_toolbar_buttons(true);
+ build_html_menu();
try {
body_classes.remove("plain");
} catch (Error error) {
@@ -1183,33 +1170,39 @@ public class ComposerWindow : Gtk.Window {
}
private void toggle_toolbar_buttons(bool show) {
- string[] buttons = {"bold button", "italic button", "underline button",
- "strikethrough button", "toolbar separator 1", "toolbar separator 2",
- "link button", "remove format button"};
- foreach (string button in buttons) {
- Gtk.Widget widget = (Gtk.Widget) builder.get_object(button);
- if (show)
- widget.show();
- else
- widget.hide();
- }
+ actions.get_action(ACTION_BOLD).visible =
+ actions.get_action(ACTION_ITALIC).visible =
+ actions.get_action(ACTION_UNDERLINE).visible =
+ actions.get_action(ACTION_STRIKETHROUGH).visible =
+ actions.get_action(ACTION_INSERT_LINK).visible =
+ actions.get_action(ACTION_REMOVE_FORMAT).visible = show;
}
- private void on_open_menu() {
- if (!menu_button.active)
- return;
+ private void build_plaintext_menu() {
+ GtkUtil.clear_menu(menu);
- if (compose_as_html) {
- menu_html.show_all();
- menu_html.popup(null, null, GtkUtil.menu_popup_relative, 0, 0);
- } else {
- menu_plain.show_all();
- menu_plain.popup(null, null, GtkUtil.menu_popup_relative, 0, 0);
- }
+ menu.append(html_item2);
+ menu.show_all();
}
- private void on_deactivate_menu() {
- menu_button.active = false;
+ private void build_html_menu() {
+ GtkUtil.clear_menu(menu);
+
+ menu.append(font_sans);
+ menu.append(font_serif);
+ menu.append(font_monospace);
+ menu.append(new Gtk.SeparatorMenuItem());
+
+ menu.append(font_small);
+ menu.append(font_medium);
+ menu.append(font_large);
+ menu.append(new Gtk.SeparatorMenuItem());
+
+ menu.append(color_item);
+ menu.append(new Gtk.SeparatorMenuItem());
+
+ menu.append(html_item);
+ menu.show_all(); // Call this or only menu items associated with actions will be displayed.
}
private void on_font_sans() {
diff --git a/src/client/folder-list/folder-list-account-branch.vala b/src/client/folder-list/folder-list-account-branch.vala
index bd40a425..e1fef46e 100644
--- a/src/client/folder-list/folder-list-account-branch.vala
+++ b/src/client/folder-list/folder-list-account-branch.vala
@@ -7,7 +7,6 @@
// A branch that holds all the folders for a particular account.
public class FolderList.AccountBranch : Sidebar.Branch {
public Geary.Account account { get; private set; }
- public SpecialGrouping uncommon_special_group { get; private set; }
public SpecialGrouping user_folder_group { get; private set; }
public Gee.HashMap folder_entries { get; private set; }
@@ -16,15 +15,12 @@ public class FolderList.AccountBranch : Sidebar.Branch {
Sidebar.Branch.Options.NONE, normal_folder_comparator, special_folder_comparator);
this.account = account;
- uncommon_special_group = new SpecialGrouping(1, _("More"),
- new ThemedIcon("folder-open"), new ThemedIcon("folder"));
user_folder_group = new SpecialGrouping(2, "",
- IconFactory.instance.get_custom_icon("tags", IconFactory.ICON_SIDEBAR));
+ IconFactory.instance.get_custom_icon("tag-symbolic", IconFactory.ICON_SIDEBAR));
folder_entries = new Gee.HashMap();
account.information.notify["nickname"].connect(on_nicknamed_changed);
- graft(get_root(), uncommon_special_group, special_folder_comparator);
graft(get_root(), user_folder_group);
}
@@ -84,20 +80,8 @@ public class FolderList.AccountBranch : Sidebar.Branch {
if (special_folder_type == Geary.SpecialFolderType.SEARCH)
return; // Don't show search folder under the account.
- switch (special_folder_type) {
- // These special folders go in the root of the account.
- case Geary.SpecialFolderType.INBOX:
- case Geary.SpecialFolderType.FLAGGED:
- case Geary.SpecialFolderType.IMPORTANT:
- case Geary.SpecialFolderType.ALL_MAIL:
- graft_point = get_root();
- break;
-
- // Others go in the "More" grouping.
- default:
- graft_point = uncommon_special_group;
- break;
- }
+ // Special folders go in the root of the account.
+ graft_point = get_root();
} else if (folder.path.get_parent() == null) {
// Top-level folders get put in our special user folders group.
graft_point = user_folder_group;
diff --git a/src/client/folder-list/folder-list-folder-entry.vala b/src/client/folder-list/folder-list-folder-entry.vala
index 99156eb8..18305c8c 100644
--- a/src/client/folder-list/folder-list-folder-entry.vala
+++ b/src/client/folder-list/folder-list-folder-entry.vala
@@ -46,34 +46,34 @@ public class FolderList.FolderEntry : FolderList.AbstractFolderEntry, Sidebar.In
public override Icon? get_sidebar_icon() {
switch (folder.special_folder_type) {
case Geary.SpecialFolderType.NONE:
- return IconFactory.instance.get_custom_icon("tag", IconFactory.ICON_SIDEBAR);
+ return IconFactory.instance.get_custom_icon("tag-symbolic", IconFactory.ICON_SIDEBAR);
case Geary.SpecialFolderType.INBOX:
- return new ThemedIcon("mail-inbox");
+ return new ThemedIcon("inbox-symbolic");
case Geary.SpecialFolderType.DRAFTS:
- return new ThemedIcon("accessories-text-editor");
+ return new ThemedIcon("accessories-text-editor-symbolic");
case Geary.SpecialFolderType.SENT:
- return new ThemedIcon("mail-sent");
+ return new ThemedIcon("sent-symbolic");
case Geary.SpecialFolderType.FLAGGED:
- return new ThemedIcon("starred");
+ return new ThemedIcon("star-symbolic");
case Geary.SpecialFolderType.IMPORTANT:
- return new ThemedIcon("task-due");
+ return new ThemedIcon("task-due-symbolic");
case Geary.SpecialFolderType.ALL_MAIL:
- return IconFactory.instance.get_custom_icon("mail-archive", IconFactory.ICON_SIDEBAR);
+ return IconFactory.instance.get_custom_icon("archive-symbolic", IconFactory.ICON_SIDEBAR);
case Geary.SpecialFolderType.SPAM:
- return new ThemedIcon("mail-mark-junk");
+ return new ThemedIcon("spam-symbolic");
case Geary.SpecialFolderType.TRASH:
- return new ThemedIcon("user-trash");
+ return new ThemedIcon("user-trash-symbolic");
case Geary.SpecialFolderType.OUTBOX:
- return new ThemedIcon("mail-outbox");
+ return new ThemedIcon("outbox-symbolic");
default:
assert_not_reached();
diff --git a/src/client/geary-controller.vala b/src/client/geary-controller.vala
index e31df608..23ba345a 100644
--- a/src/client/geary-controller.vala
+++ b/src/client/geary-controller.vala
@@ -40,12 +40,12 @@ public class GearyController : Geary.BaseObject {
private const string DELETE_MESSAGE_LABEL = _("_Delete");
private const string DELETE_MESSAGE_TOOLTIP_SINGLE = _("Delete conversation (Delete, Backspace, A)");
private const string DELETE_MESSAGE_TOOLTIP_MULTIPLE = _("Delete conversations (Delete, Backspace, A)");
- private const string DELETE_MESSAGE_ICON_NAME = "user-trash-full";
+ private const string DELETE_MESSAGE_ICON_NAME = "user-trash-symbolic";
private const string ARCHIVE_MESSAGE_LABEL = _("_Archive");
private const string ARCHIVE_MESSAGE_TOOLTIP_SINGLE = _("Archive conversation (Delete, Backspace, A)");
private const string ARCHIVE_MESSAGE_TOOLTIP_MULTIPLE = _("Archive conversations (Delete, Backspace, A)");
- private const string ARCHIVE_MESSAGE_ICON_NAME = "mail-archive";
+ private const string ARCHIVE_MESSAGE_ICON_NAME = "archive-symbolic";
private const string MARK_AS_SPAM_LABEL = _("Mark as s_pam");
private const string MARK_AS_NOT_SPAM_LABEL = _("Mark as not s_pam");
@@ -164,7 +164,7 @@ public class GearyController : Geary.BaseObject {
main_window.conversation_list_view.conversations_selected.connect(on_conversations_selected);
main_window.conversation_list_view.conversation_activated.connect(on_conversation_activated);
main_window.conversation_list_view.load_more.connect(on_load_more);
- main_window.conversation_list_view.mark_conversation.connect(on_mark_conversation);
+ main_window.conversation_list_view.mark_conversations.connect(on_mark_conversations);
main_window.conversation_list_view.visible_conversations_changed.connect(on_visible_conversations_changed);
main_window.folder_list.folder_selected.connect(on_folder_selected);
main_window.folder_list.copy_conversation.connect(on_copy_conversation);
@@ -253,7 +253,7 @@ public class GearyController : Geary.BaseObject {
quit.label = _("_Quit");
entries += quit;
- Gtk.ActionEntry mark_menu = { ACTION_MARK_AS_MENU, null, TRANSLATABLE, null, null,
+ Gtk.ActionEntry mark_menu = { ACTION_MARK_AS_MENU, null, TRANSLATABLE, null, _("Mark conversation"),
on_show_mark_menu };
mark_menu.label = _("_Mark as...");
mark_menu.tooltip = MARK_MESSAGE_MENU_TOOLTIP_SINGLE;
@@ -271,7 +271,7 @@ public class GearyController : Geary.BaseObject {
entries += mark_unread;
add_accelerator("U", ACTION_MARK_AS_UNREAD);
- Gtk.ActionEntry mark_starred = { ACTION_MARK_AS_STARRED, "starred", TRANSLATABLE, "S", null,
+ Gtk.ActionEntry mark_starred = { ACTION_MARK_AS_STARRED, "star-symbolic", TRANSLATABLE, "S", null,
on_mark_as_starred };
mark_starred.label = _("_Star");
entries += mark_starred;
@@ -287,35 +287,33 @@ public class GearyController : Geary.BaseObject {
entries += mark_spam;
add_accelerator("exclam", ACTION_MARK_AS_SPAM); // Exclamation mark (!)
- Gtk.ActionEntry copy_menu = { ACTION_COPY_MENU, null, TRANSLATABLE, "L", null, null };
+ Gtk.ActionEntry copy_menu = { ACTION_COPY_MENU, null, TRANSLATABLE, "L",
+ _("Add label"), null };
copy_menu.label = _("_Label");
entries += copy_menu;
- Gtk.ActionEntry move_menu = { ACTION_MOVE_MENU, null, TRANSLATABLE, "M", null, null };
+ Gtk.ActionEntry move_menu = { ACTION_MOVE_MENU, null, TRANSLATABLE, "M", _("Move conversation"), null };
move_menu.label = _("_Move");
entries += move_menu;
- Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, null, TRANSLATABLE, "N", null,
- on_new_message };
- new_message.label = _("_New Message");
+ Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, null, null, "N",
+ _("Compose new message (Ctrl+N, N)"), on_new_message };
entries += new_message;
add_accelerator("N", ACTION_NEW_MESSAGE);
- Gtk.ActionEntry reply_to_message = { ACTION_REPLY_TO_MESSAGE, null, TRANSLATABLE, "R",
- null, on_reply_to_message_action };
- reply_to_message.label = _("_Reply");
+ Gtk.ActionEntry reply_to_message = { ACTION_REPLY_TO_MESSAGE, null, null, "R",
+ _("Reply (Ctrl+R, R)"), on_reply_to_message_action };
entries += reply_to_message;
add_accelerator("R", ACTION_REPLY_TO_MESSAGE);
- Gtk.ActionEntry reply_all_message = { ACTION_REPLY_ALL_MESSAGE, null, TRANSLATABLE,
- "R", null, on_reply_all_message_action };
- reply_all_message.label = _("Reply _all");
+ Gtk.ActionEntry reply_all_message = { ACTION_REPLY_ALL_MESSAGE, null, null,
+ "R", _("Reply all (Ctrl+Shift+R, Shift+R)"),
+ on_reply_all_message_action };
entries += reply_all_message;
add_accelerator("R", ACTION_REPLY_ALL_MESSAGE);
- Gtk.ActionEntry forward_message = { ACTION_FORWARD_MESSAGE, null, TRANSLATABLE, "L", null,
- on_forward_message_action };
- forward_message.label = _("_Forward");
+ Gtk.ActionEntry forward_message = { ACTION_FORWARD_MESSAGE, null, null, "L",
+ _("Forward (Ctrl+L, F)"), on_forward_message_action };
entries += forward_message;
add_accelerator("F", ACTION_FORWARD_MESSAGE);
@@ -362,7 +360,7 @@ public class GearyController : Geary.BaseObject {
private Gtk.ToggleActionEntry[] create_toggle_actions() {
Gtk.ToggleActionEntry[] entries = new Gtk.ToggleActionEntry[0];
- Gtk.ToggleActionEntry gear_menu = { ACTION_GEAR_MENU, null, _("Menu"), "F10",
+ Gtk.ToggleActionEntry gear_menu = { ACTION_GEAR_MENU, null, null, "F10",
null, null, false };
entries += gear_menu;
@@ -1079,6 +1077,16 @@ public class GearyController : Geary.BaseObject {
return add_to;
}
+ private Gee.Collection get_conversation_collection_email_ids(
+ Gee.Collection conversations, bool preview_message_only = false) {
+ Gee.ArrayList ret = new Gee.ArrayList();
+
+ foreach(Geary.App.Conversation c in conversations)
+ get_conversation_email_ids(c, preview_message_only, ret);
+
+ return ret;
+ }
+
private Gee.ArrayList get_selected_email_ids(
bool preview_messages_only) {
Gee.ArrayList ids = new Gee.ArrayList();
@@ -1177,12 +1185,13 @@ public class GearyController : Geary.BaseObject {
}
}
- private void on_mark_conversation(Geary.App.Conversation conversation,
- Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, bool only_mark_preview) {
- mark_email(get_conversation_email_ids(conversation, only_mark_preview,
- new Gee.ArrayList()), flags_to_add, flags_to_remove);
+ private void on_mark_conversations(Gee.Collection conversations,
+ Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove,
+ bool only_mark_preview = false) {
+ mark_email(get_conversation_collection_email_ids(conversations, only_mark_preview),
+ flags_to_add, flags_to_remove);
}
-
+
private void on_conversation_viewer_mark_messages(Gee.Collection emails,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove) {
mark_email(emails, flags_to_add, flags_to_remove);
@@ -1738,5 +1747,12 @@ public class GearyController : Geary.BaseObject {
return false;
}
+
+ /**
+ * Returns a read-only set of currently selected conversations.
+ */
+ public Gee.Set get_selected_conversations() {
+ return selected_conversations.read_only_view;
+ }
}
diff --git a/src/client/sidebar/sidebar-tree.vala b/src/client/sidebar/sidebar-tree.vala
index 71726d6c..7e6331ba 100644
--- a/src/client/sidebar/sidebar-tree.vala
+++ b/src/client/sidebar/sidebar-tree.vala
@@ -799,7 +799,12 @@ public class Sidebar.Tree : Gtk.TreeView {
if (info == null)
return null;
- icon = info.load_icon();
+ // If the icon's symbolic, make it black.
+ Gdk.RGBA black = Gdk.RGBA();
+ black.red = black.green = black.blue = 0.0;
+ black.alpha = 1.0;
+ icon = info.load_symbolic(black);
+
if (icon == null)
return null;
diff --git a/src/client/ui/folder-menu.vala b/src/client/ui/folder-menu.vala
index 44421eca..8dd5b2aa 100644
--- a/src/client/ui/folder-menu.vala
+++ b/src/client/ui/folder-menu.vala
@@ -4,18 +4,12 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
-public class FolderMenu : GtkUtil.ToggleToolbarDropdown {
+public class FolderMenu : Gtk.Menu {
private Gee.List folder_list = new Gee.ArrayList();
public signal void folder_selected(Geary.Folder folder);
- public FolderMenu(Icon icon, Gtk.IconSize icon_size, Gtk.Menu? supplied_menu,
- Gtk.Menu? supplied_proxy_menu) {
- base (icon, icon_size, supplied_menu, supplied_proxy_menu);
-
- // TODO Add fancy filter option.
- // TODO Make the menu items checkboxes instead of buttons.
- // TODO Merge the move/copy menus and just have a move/copy buttons at bottom of this menu.
+ public FolderMenu() {
}
public bool has_folder(Geary.Folder folder) {
@@ -27,32 +21,25 @@ public class FolderMenu : GtkUtil.ToggleToolbarDropdown {
folder_list.sort(folder_sort);
int index = folder_list.index_of(folder);
- menu.insert(build_menu_item(folder), index);
- proxy_menu.insert(build_menu_item(folder), index);
+ insert(build_menu_item(folder), index);
- menu.show_all();
- proxy_menu.show_all();
+ show_all();
}
public void remove_folder(Geary.Folder folder) {
int index = folder_list.index_of(folder);
folder_list.remove(folder);
- if (index >= 0) {
- menu.remove(menu.get_children().nth_data(index));
- proxy_menu.remove(proxy_menu.get_children().nth_data(index));
- }
+ if (index >= 0)
+ remove(get_children().nth_data(index));
- menu.show_all();
- proxy_menu.show_all();
+ show_all();
}
public void clear() {
folder_list.clear();
- menu.foreach((w) => menu.remove(w));
- proxy_menu.foreach((w) => proxy_menu.remove(w));
- menu.show_all();
- proxy_menu.show_all();
+ this.foreach((w) => remove(w));
+ show_all();
}
private Gtk.MenuItem build_menu_item(Geary.Folder folder) {
diff --git a/src/client/ui/icon-factory.vala b/src/client/ui/icon-factory.vala
index 365c6633..cfed5806 100644
--- a/src/client/ui/icon-factory.vala
+++ b/src/client/ui/icon-factory.vala
@@ -26,10 +26,15 @@ public class IconFactory {
public const int UNREAD_ICON_SIZE = 16;
public Gdk.Pixbuf unread { get; private set; }
+ public Gdk.Pixbuf read { get; private set; }
+ public Gdk.Pixbuf unread_colored { get; private set; }
+ public Gdk.Pixbuf read_colored { get; private set; }
public const int STAR_ICON_SIZE = 16;
public Gdk.Pixbuf starred { get; private set; }
public Gdk.Pixbuf unstarred { get; private set; }
+ public Gdk.Pixbuf starred_colored { get; private set; }
+ public Gdk.Pixbuf unstarred_colored { get; private set; }
private Gtk.IconTheme icon_theme { get; private set; }
@@ -48,9 +53,19 @@ public class IconFactory {
// Load icons here.
application_icon = load("geary", APPLICATION_ICON_SIZE);
- unread = load("mail-unread", UNREAD_ICON_SIZE);
- starred = load("starred", STAR_ICON_SIZE);
- unstarred = load("non-starred-grey", STAR_ICON_SIZE);
+ unread = load("unread-symbolic", UNREAD_ICON_SIZE);
+ read = load("read-symbolic", UNREAD_ICON_SIZE);
+ starred = load("star-symbolic", STAR_ICON_SIZE);
+ unstarred = load("unstarred-symbolic", STAR_ICON_SIZE);
+
+ Gdk.RGBA gray_color = Gdk.RGBA();
+ gray_color.parse(FormattedConversationData.UNREAD_BG_COLOR);
+
+ // Load pre-colored symbolic icons here.
+ read_colored = load_symbolic_colored("read-symbolic", UNREAD_ICON_SIZE, gray_color);
+ unread_colored = load_symbolic_colored("unread-symbolic", STAR_ICON_SIZE, gray_color);
+ starred_colored = load_symbolic_colored("star-symbolic", STAR_ICON_SIZE, gray_color);
+ unstarred_colored = load_symbolic_colored("unstarred-symbolic", STAR_ICON_SIZE, gray_color);
}
public void init() {
@@ -76,7 +91,15 @@ public class IconFactory {
public Icon get_custom_icon(string name, Gtk.IconSize size) {
int pixels = icon_size_to_pixels(size);
- return new FileIcon(icons_dir.get_child("%dx%d".printf(pixels, pixels)).get_child("%s.svg".printf(name)));
+ // Try sized icon first.
+ File icon_file = icons_dir.get_child("%dx%d".printf(pixels, pixels)).get_child(
+ "%s.svg".printf(name));
+
+ // If that wasn't found, try a non-sized icon.
+ if (!icon_file.query_exists())
+ icon_file = icons_dir.get_child("%s.svg".printf(name));
+
+ return new FileIcon(icon_file);
}
private void append_icons_search_path(string? name) {
@@ -100,13 +123,18 @@ public class IconFactory {
debug("Unable to lookup icon %s, falling back to image-missing...", icon_name);
}
- // If that fails, try the missing image icon instead.
+ // Default: missing image icon.
+ return get_missing_icon(size, flags);
+ }
+
+ // Attempts to load and return the missing image icon.
+ private Gdk.Pixbuf? get_missing_icon(int size, Gtk.IconLookupFlags flags = 0) {
try {
return icon_theme.load_icon("image-missing", size, flags);
} catch (Error err) {
warning("Couldn't load image-missing icon: %s", err.message);
}
-
+
// If that fails... well they're out of luck.
return null;
}
@@ -114,7 +142,35 @@ public class IconFactory {
public Gtk.IconInfo? lookup_icon(string icon_name, int size, Gtk.IconLookupFlags flags = 0) {
Gtk.IconInfo? icon_info = icon_theme.lookup_icon(icon_name, size, flags);
return icon_info != null ? icon_info.copy() :
- icon_theme.lookup_icon("image-missing", size, flags);
+ icon_theme.lookup_icon("document-symbolic", size, flags);
+ }
+
+ /**
+ * Loads a symbolic icon into a pixbuf, where the color-key has been switched to the provided
+ * color, or black if no color is set.
+ */
+ public Gdk.Pixbuf? load_symbolic_colored(string icon_name, int size, Gdk.RGBA? color = null,
+ Gtk.IconLookupFlags flags = 0) {
+ Gtk.IconInfo? icon_info = icon_theme.lookup_icon(icon_name, size, flags);
+
+ // Default to black if no color provided.
+ if (color == null) {
+ color = Gdk.RGBA();
+ color.red = color.green = color.blue = 0.0;
+ color.alpha = 1.0;
+ }
+
+ // Attempt to load as a symbolic icon.
+ if (icon_info != null) {
+ try {
+ return icon_info.load_symbolic(color);
+ } catch (Error e) {
+ warning("Couldn't load icon: %s", e.message);
+ }
+ }
+
+ // Default: missing image icon.
+ return get_missing_icon(size, flags);
}
}
diff --git a/src/client/ui/main-toolbar.vala b/src/client/ui/main-toolbar.vala
index 1d37d338..d41c24b0 100644
--- a/src/client/ui/main-toolbar.vala
+++ b/src/client/ui/main-toolbar.vala
@@ -5,118 +5,89 @@
*/
// Draws the main toolbar.
-public class MainToolbar : Gtk.Box {
+public class MainToolbar : PillToolbar {
private const string ICON_CLEAR_NAME = "edit-clear-symbolic";
private const string DEFAULT_SEARCH_TEXT = _("Search");
- private Gtk.Toolbar toolbar;
- public FolderMenu copy_folder_menu { get; private set; }
- public FolderMenu move_folder_menu { get; private set; }
+ public FolderMenu copy_folder_menu { get; private set; default = new FolderMenu(); }
+ public FolderMenu move_folder_menu { get; private set; default = new FolderMenu(); }
public string search_text { get { return search_entry.text; } }
- private GtkUtil.ToggleToolbarDropdown mark_menu_dropdown;
- private GtkUtil.ToggleToolbarDropdown app_menu_dropdown;
- private Gtk.ToolItem search_container;
- private Gtk.Entry search_entry;
+ private Gtk.ToolItem search_container = new Gtk.ToolItem();
+ private Gtk.Entry search_entry = new Gtk.Entry();
private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
private MonitoredProgressBar search_upgrade_progress_bar = new MonitoredProgressBar();
public signal void search_text_changed(string search_text);
public MainToolbar() {
- Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
-
+ base(GearyApplication.instance.actions);
GearyApplication.instance.controller.account_selected.connect(on_account_changed);
- Gtk.Builder builder = GearyApplication.instance.create_builder("toolbar.glade");
- toolbar = builder.get_object("toolbar") as Gtk.Toolbar;
-
- // Setup each of the normal toolbar buttons.
- set_toolbutton_action(builder, GearyController.ACTION_NEW_MESSAGE);
- set_toolbutton_action(builder, GearyController.ACTION_REPLY_TO_MESSAGE);
- set_toolbutton_action(builder, GearyController.ACTION_REPLY_ALL_MESSAGE);
- set_toolbutton_action(builder, GearyController.ACTION_FORWARD_MESSAGE);
- set_toolbutton_action(builder, GearyController.ACTION_DELETE_MESSAGE, true);
-
- // Setup the folder menus (move/copy).
-
- Gtk.ToggleToolButton copy_toggle_button = set_toolbutton_action(builder,
- GearyController.ACTION_COPY_MENU) as Gtk.ToggleToolButton;
- copy_folder_menu = new FolderMenu(
- IconFactory.instance.get_custom_icon("tag-new", IconFactory.ICON_TOOLBAR),
- Gtk.IconSize.LARGE_TOOLBAR, null, null);
- copy_folder_menu.attach(copy_toggle_button);
-
- Gtk.ToggleToolButton move_toggle_button = set_toolbutton_action(builder,
- GearyController.ACTION_MOVE_MENU) as Gtk.ToggleToolButton;
- move_folder_menu = new FolderMenu(
- IconFactory.instance.get_custom_icon("mail-move", IconFactory.ICON_TOOLBAR),
- Gtk.IconSize.LARGE_TOOLBAR, null, null);
- move_folder_menu.attach(move_toggle_button);
-
- // Assemble mark menu button.
+ // Assemble mark menu.
GearyApplication.instance.load_ui_file("toolbar_mark_menu.ui");
- Gtk.Menu mark_menu = GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMarkMenu")
- as Gtk.Menu;
- Gtk.Menu mark_proxy_menu = (Gtk.Menu) GearyApplication.instance.ui_manager
- .get_widget("/ui/ToolbarMarkMenuProxy");
- Gtk.ToggleToolButton mark_menu_button = set_toolbutton_action(builder,
- GearyController.ACTION_MARK_AS_MENU) as Gtk.ToggleToolButton;
- mark_menu_dropdown = new GtkUtil.ToggleToolbarDropdown(
- IconFactory.instance.get_custom_icon("edit-mark", IconFactory.ICON_TOOLBAR),
- Gtk.IconSize.LARGE_TOOLBAR, mark_menu, mark_proxy_menu);
- mark_menu_dropdown.attach(mark_menu_button);
-
- // Ensure that shortcut keys are drawn in the mark menu
+ Gtk.Menu mark_menu = (Gtk.Menu) GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMarkMenu");
mark_menu.foreach(GtkUtil.show_menuitem_accel_labels);
+ // Setup the application menu.
+ GearyApplication.instance.load_ui_file("toolbar_menu.ui");
+ Gtk.Menu application_menu = (Gtk.Menu) GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMenu");
+ application_menu.foreach(GtkUtil.show_menuitem_accel_labels);
+
+ // Toolbar setup.
+ orientation = Gtk.Orientation.HORIZONTAL;
+ get_style_context().add_class(Gtk.STYLE_CLASS_MENUBAR); // Drag window via toolbar.
+ Gee.List insert = new Gee.ArrayList();
+
+ // Compose.
+ insert.add(create_toolbar_button("text-editor-symbolic", GearyController.ACTION_NEW_MESSAGE));
+ add(create_pill_buttons(insert, false));
+
+ // Reply buttons
+ insert.clear();
+ insert.add(create_toolbar_button("reply-symbolic", GearyController.ACTION_REPLY_TO_MESSAGE));
+ insert.add(create_toolbar_button("reply-all-symbolic", GearyController.ACTION_REPLY_ALL_MESSAGE));
+ insert.add(create_toolbar_button("forward-symbolic", GearyController.ACTION_FORWARD_MESSAGE));
+ add(create_pill_buttons(insert));
+
+ // Mark, copy, move.
+ insert.clear();
+ insert.add(create_menu_button("marker-symbolic", mark_menu, GearyController.ACTION_MARK_AS_MENU));
+ insert.add(create_menu_button("tag-symbolic", copy_folder_menu, GearyController.ACTION_COPY_MENU));
+ insert.add(create_menu_button("folder-symbolic", move_folder_menu, GearyController.ACTION_MOVE_MENU));
+ add(create_pill_buttons(insert));
+
+ // Archive/delete button.
+ // For this button, the controller sets the tooltip and icon depending on the context.
+ insert.clear();
+ insert.add(create_toolbar_button("", GearyController.ACTION_DELETE_MESSAGE));
+ add(create_pill_buttons(insert));
+
+ // Spacer.
+ add(create_spacer());
+
// Search bar.
- search_container = (Gtk.ToolItem) builder.get_object("search_container");
- search_entry = (Gtk.Entry) builder.get_object("search_entry");
+ search_entry.width_chars = 32;
+ search_entry.primary_icon_name = "edit-find-symbolic";
+ search_entry.secondary_icon_name = "edit-clear-symbolic";
+ search_entry.secondary_icon_activatable = true;
+ search_entry.secondary_icon_sensitive = true;
search_entry.changed.connect(on_search_entry_changed);
search_entry.icon_release.connect(on_search_entry_icon_release);
search_entry.key_press_event.connect(on_search_key_press);
on_search_entry_changed(); // set initial state
search_entry.has_focus = true;
+ search_container.add(search_entry);
+ add(search_container);
- // Setup the application menu.
- GearyApplication.instance.load_ui_file("toolbar_menu.ui");
- Gtk.Menu application_menu = GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMenu")
- as Gtk.Menu;
+ // Application button.
+ insert.clear();
+ insert.add(create_menu_button("emblem-system-symbolic", application_menu, GearyController.ACTION_GEAR_MENU));
+ add(create_pill_buttons(insert));
- // Ensure that shortcut keys are drawn in the gear menu
- application_menu.foreach(GtkUtil.show_menuitem_accel_labels);
-
- Gtk.Menu application_proxy_menu = GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMenuProxy")
- as Gtk.Menu;
- Gtk.ToggleToolButton app_menu_button = set_toolbutton_action(builder, GearyController.ACTION_GEAR_MENU)
- as Gtk.ToggleToolButton;
- app_menu_dropdown = new GtkUtil.ToggleToolbarDropdown(
- IconFactory.instance.get_theme_icon("application-menu"), Gtk.IconSize.LARGE_TOOLBAR,
- application_menu, application_proxy_menu);
- app_menu_dropdown.show_arrow = false;
- app_menu_dropdown.attach(app_menu_button);
-
- toolbar.get_style_context().add_class("primary-toolbar");
-
- search_upgrade_progress_bar.show_text = true;
- search_upgrade_progress_bar.margin_top = search_upgrade_progress_bar.margin_bottom = 3;
-
- add(toolbar);
set_search_placeholder_text(DEFAULT_SEARCH_TEXT);
}
- private Gtk.ToolButton set_toolbutton_action(Gtk.Builder builder, string action,
- bool use_action_appearance = false) {
- Gtk.ToolButton button = builder.get_object(action) as Gtk.ToolButton;
-
- // Must manually set use_action_appearance to false until Glade re-adds this feature.
- // See this ticket: https://bugzilla.gnome.org/show_bug.cgi?id=694407#c11
- button.use_action_appearance = use_action_appearance;
- button.set_related_action(GearyApplication.instance.actions.get_action(action));
- return button;
- }
-
public void set_search_text(string text) {
search_entry.text = text;
}
diff --git a/src/client/ui/main-window.vala b/src/client/ui/main-window.vala
index ae44fa1b..77ff0e24 100644
--- a/src/client/ui/main-window.vala
+++ b/src/client/ui/main-window.vala
@@ -103,27 +103,37 @@ public class MainWindow : Gtk.Window {
folder_list_scrolled.set_size_request(FOLDER_LIST_WIDTH, -1);
folder_list_scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
folder_list_scrolled.add(folder_list);
+ Gtk.Frame folder_frame = new Gtk.Frame(null);
+ folder_frame.shadow_type = Gtk.ShadowType.IN;
+ folder_frame.add(folder_list_scrolled);
// message list
conversation_list_scrolled = new Gtk.ScrolledWindow(null, null);
conversation_list_scrolled.set_size_request(MESSAGE_LIST_WIDTH, -1);
conversation_list_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
conversation_list_scrolled.add(conversation_list_view);
+ Gtk.Frame conversation_frame = new Gtk.Frame(null);
+ conversation_frame.shadow_type = Gtk.ShadowType.IN;
+ conversation_frame.add(conversation_list_scrolled);
// Three-pane display.
Gtk.Box status_bar_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
Gtk.Statusbar status_bar = new Gtk.Statusbar();
status_bar.add(spinner);
- status_bar_box.pack_start(folder_list_scrolled);
+ status_bar_box.pack_start(folder_frame);
status_bar_box.pack_start(status_bar, false, false, 0);
#if !HAVE_LIBGRANITE
folder_paned.get_style_context().add_class("sidebar-pane-separator");
#endif
+ Gtk.Frame viewer_frame = new Gtk.Frame(null);
+ viewer_frame.shadow_type = Gtk.ShadowType.IN;
+ viewer_frame.add(conversation_viewer);
+
// Message list left of message viewer.
- conversations_paned.pack1(conversation_list_scrolled, false, false);
- conversations_paned.pack2(conversation_viewer, true, true);
+ conversations_paned.pack1(conversation_frame, false, false);
+ conversations_paned.pack2(viewer_frame, true, true);
// Folder list to the left of everything.
folder_paned.pack1(status_bar_box, false, false);
diff --git a/src/client/ui/pill-toolbar.vala b/src/client/ui/pill-toolbar.vala
new file mode 100644
index 00000000..2c4ddc2e
--- /dev/null
+++ b/src/client/ui/pill-toolbar.vala
@@ -0,0 +1,117 @@
+/* Copyright 2011-2013 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * Class for creating a Nautilus-style "pill" toolbar. Use only as directed.
+ */
+public class PillToolbar : Gtk.Toolbar {
+ private Gtk.ActionGroup action_group;
+
+ public PillToolbar(Gtk.ActionGroup toolbar_action_group) {
+ action_group = toolbar_action_group;
+ }
+
+ private void setup_button(Gtk.Button b, string? icon_name, string action_name) {
+ b.related_action = action_group.get_action(action_name);
+ b.tooltip_text = b.related_action.tooltip;
+ b.image = new Gtk.Image.from_icon_name(icon_name != null ? icon_name :
+ b.related_action.icon_name, Gtk.IconSize.MENU);
+ b.always_show_image = true;
+ b.image.margin = get_icon_margin();
+
+ if (!Geary.String.is_empty(b.related_action.label))
+ b.image.margin_right += 4;
+ }
+
+ /**
+ * Given an icon and action, creates a button that triggers the action.
+ */
+ public Gtk.Button create_toolbar_button(string? icon_name, string action_name) {
+ Gtk.Button b = new Gtk.Button();
+ setup_button(b, icon_name, action_name);
+
+ return b;
+ }
+
+ /**
+ * Given an icon and action, creates a toggle button that triggers the action.
+ */
+ public Gtk.Button create_toggle_button(string? icon_name, string action_name) {
+ Gtk.ToggleButton b = new Gtk.ToggleButton();
+ setup_button(b, icon_name, action_name);
+
+ return b;
+ }
+
+ /**
+ * Given an icon, menu, and action, creates a button that triggers the menu and the action.
+ */
+ public Gtk.MenuButton create_menu_button(string? icon_name, Gtk.Menu? menu, string action_name) {
+ Gtk.MenuButton b = new Gtk.MenuButton();
+ setup_button(b, icon_name, action_name);
+ b.popup = menu;
+
+ return b;
+ }
+
+ /**
+ * Given a list of buttons, creates a "pill-style" tool item that can be appended to this
+ * toolbar. Optionally adds a spacer to the left.
+ */
+ public Gtk.ToolItem create_pill_buttons(Gee.Collection buttons,
+ bool left_spacer = true, bool right_spacer = false) {
+ Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
+
+ if (buttons.size > 1) {
+ box.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
+ box.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED);
+ }
+
+ int i = 0;
+ foreach(Gtk.Button button in buttons) {
+ box.add(button);
+
+ // Place the right spacer on the button itself. This way if the button is not displayed,
+ // the spacer will not appear.
+ if (i == buttons.size - 1 && right_spacer)
+ button.set_margin_right(12);
+
+ i++;
+ }
+
+ Gtk.ToolItem tool_item = new Gtk.ToolItem();
+ tool_item.add(box);
+
+ if (left_spacer)
+ box.set_margin_left(12);
+
+ return tool_item;
+ }
+
+ /**
+ * Computes the margin for each icon (shamelessly stolen from Nautilus.)
+ */
+ public int get_icon_margin() {
+ Gtk.IconSize toolbar_size = get_icon_size();
+ int toolbar_size_px, menu_size_px;
+
+ Gtk.icon_size_lookup(Gtk.IconSize.MENU, out menu_size_px, null);
+ Gtk.icon_size_lookup(toolbar_size, out toolbar_size_px, null);
+
+ return Geary.Numeric.int_floor((int) ((toolbar_size_px - menu_size_px) / 2.0), 0);
+ }
+
+ /**
+ * Returns an expandable spacer item.
+ */
+ public Gtk.ToolItem create_spacer() {
+ Gtk.ToolItem spacer = new Gtk.ToolItem();
+ spacer.set_expand(true);
+
+ return spacer;
+ }
+}
+
diff --git a/src/client/util/util-gtk.vala b/src/client/util/util-gtk.vala
index 8edfd46a..6cc8d2eb 100644
--- a/src/client/util/util-gtk.vala
+++ b/src/client/util/util-gtk.vala
@@ -6,163 +6,6 @@
namespace GtkUtil {
-// The reason GtkUtil.ToggleToolbarDropdown exists (rather than using Gtk.MenuToolButton) is that
-// the latter creates two separate buttons, one for the icon (which activates the "default" action)
-// and one for the arrow (which presents the dropdown menu). We need a single button that shows
-// the dropdown menu only, hence this version.
-//
-// In order to use this, create a Gtk.ToggleToolButton and call attach().
-//
-// TODO: An better solution would be for this to subclass Gtk.ToggleToolButton and register class
-// with Gtk.Builder and Glade.
-//
-// TODO: Would be better to get the icon from the ToggleToolbarButton (could do this even without
-// above improvement), but unlike the label, that's not so straightforward due to the number of
-// ways of representing an icon in GTK.
-
-public class ToggleToolbarDropdown : Geary.BaseObject {
- public const int DEFAULT_PADDING = 2;
-
- public bool show_arrow { get; set; default = true; }
- public Gtk.Menu menu { get; private set; }
- public Gtk.Menu proxy_menu { get; private set; }
-
- private int padding;
- private Gtk.Image icon;
- private Gtk.Label label = new Gtk.Label(null);
- private Gtk.Arrow icon_arrow = new Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.NONE);
- private Gtk.Box icon_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
- private Gtk.Arrow label_arrow = new Gtk.Arrow(Gtk.ArrowType.DOWN, Gtk.ShadowType.NONE);
- private Gtk.Box label_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
- private Gtk.ToggleToolButton? owner = null;
-
- public ToggleToolbarDropdown(Icon icon, Gtk.IconSize icon_size, Gtk.Menu? supplied_menu,
- Gtk.Menu? supplied_proxy_menu, int padding = DEFAULT_PADDING) {
- this.padding = padding;
- this.icon = new Gtk.Image.from_gicon(icon, icon_size);
- menu = supplied_menu ?? new Gtk.Menu();
- proxy_menu = supplied_proxy_menu ?? new Gtk.Menu();
-
- // icon widget
- icon_box.pack_start(this.icon, true, true, padding);
- icon_box.pack_end(icon_arrow, true, true, padding);
- icon_box.no_show_all = true;
-
- // label widget
- label_box.pack_start(this.label, true, true, padding);
- label_box.pack_end(label_arrow, true, true, padding);
- label_box.no_show_all = true;
-
- this.icon.visible = true;
- this.icon_arrow.visible = show_arrow;
- icon_box.visible = true;
-
- this.label.visible = true;
- this.label_arrow.visible = show_arrow;
- label_box.visible = true;
-
- notify["show-arrow"].connect(on_refresh_now);
- }
-
- ~ToggleToolbarDropdown() {
- detach();
- }
-
- public void attach(Gtk.ToggleToolButton owner) {
- if (this.owner != null) {
- debug("ToggleToolbarDropdown already attached");
-
- return;
- }
-
- this.owner = owner;
-
- owner.set_icon_widget(icon_box);
- owner.set_label_widget(label_box);
-
- menu.attach_to_widget(owner, null);
- menu.deactivate.connect(on_menu_deactivated);
-
- add_proxy_menu(owner, owner.label, proxy_menu);
-
- owner.clicked.connect(on_clicked);
- owner.notify["label"].connect(on_refresh_now);
- owner.notify["active"].connect(on_refresh_now);
- owner.notify["is-important"].connect(on_refresh_now);
- owner.notify["sensitive"].connect(on_refresh_now);
- owner.toolbar_reconfigured.connect(on_refresh_now);
-
- on_refresh_now();
- }
-
- public void detach() {
- if (owner == null)
- return;
-
- owner.clicked.disconnect(on_clicked);
- owner.notify["label"].disconnect(on_refresh_now);
- owner.notify["active"].disconnect(on_refresh_now);
- owner.notify["is-important"].disconnect(on_refresh_now);
- owner.notify["sensitive"].disconnect(on_refresh_now);
- owner.toolbar_reconfigured.disconnect(on_refresh_now);
-
- this.owner = null;
- }
-
- private void on_menu_deactivated() {
- if (owner != null)
- owner.active = false;
- }
-
- private void on_clicked() {
- if (owner != null && owner.active)
- menu.popup(null, null, menu_popup_relative, 0, 0);
- }
-
- private void on_refresh_now() {
- if (owner == null)
- return;
-
- label.set_label(owner.label);
-
- icon.sensitive = owner.sensitive;
- icon_arrow.sensitive = owner.sensitive;
- label.sensitive = owner.sensitive;
- label_arrow.sensitive = owner.sensitive;
-
- switch (owner.get_toolbar_style()) {
- case Gtk.ToolbarStyle.BOTH:
- icon.visible = true;
- icon_arrow.visible = show_arrow;
- label.visible = true;
- label_arrow.visible = false;
- break;
-
- case Gtk.ToolbarStyle.ICONS:
- icon.visible = true;
- icon_arrow.visible = show_arrow;
- label.visible = false;
- label.visible = false;
- break;
-
- case Gtk.ToolbarStyle.TEXT:
- icon.visible = false;
- icon_arrow.visible = false;
- label.visible = true;
- label_arrow.visible = show_arrow;
- break;
-
- case Gtk.ToolbarStyle.BOTH_HORIZ:
- default:
- icon.visible = true;
- icon_arrow.visible = !owner.is_important && show_arrow;
- label.visible = owner.is_important;
- label_arrow.visible = owner.is_important && show_arrow;
- break;
- }
- }
-}
-
// Use this MenuPositionFunc to position a popup menu relative to a widget
// with Gtk.Menu.popup().
//
@@ -245,4 +88,13 @@ private void add_accel_to_label(Gtk.Widget widget, Gtk.AccelKey key) {
label.refetch();
}
+/**
+ * Removes all items from a menu.
+ */
+public void clear_menu(Gtk.Menu menu) {
+ GLib.List children = menu.get_children();
+ foreach (weak Gtk.Widget child in children)
+ menu.remove(child);
+}
+
}
diff --git a/src/client/views/conversation-list-view.vala b/src/client/views/conversation-list-view.vala
index bfaf8336..2540bc1d 100644
--- a/src/client/views/conversation-list-view.vala
+++ b/src/client/views/conversation-list-view.vala
@@ -29,7 +29,7 @@ public class ConversationListView : Gtk.TreeView {
enable_load_more = false;
}
- public signal void mark_conversation(Geary.App.Conversation conversation,
+ public signal void mark_conversations(Gee.Collection conversations,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, bool only_mark_preview);
public signal void visible_conversations_changed(Gee.Set visible);
@@ -143,20 +143,54 @@ public class ConversationListView : Gtk.TreeView {
if (path == null)
return false;
- // If this is an unmodified click in the top-left of the cell, it is a star-click.
+ // Handle clicks to toggle read and starred status.
if ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0 &&
(event.state & Gdk.ModifierType.CONTROL_MASK) == 0 &&
- event.type == Gdk.EventType.BUTTON_PRESS && cell_x < 25 && cell_y < 25) {
+ event.type == Gdk.EventType.BUTTON_PRESS) {
- Geary.App.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
- Geary.EmailFlags flags = new Geary.EmailFlags();
- flags.add(Geary.EmailFlags.FLAGGED);
- if (conversation.is_flagged()) {
- mark_conversation(conversation, null, flags, false);
+ // Click positions depend on whether the preview is enabled.
+ bool read_clicked = false;
+ bool star_clicked = false;
+ if (GearyApplication.instance.config.display_preview) {
+ read_clicked = cell_x < 25 && cell_y >= 14 && cell_y <= 30;
+ star_clicked = cell_x < 25 && cell_y >= 40 && cell_y <= 62;
} else {
- mark_conversation(conversation, flags, null, true);
+ read_clicked = cell_x < 25 && cell_y >= 8 && cell_y <= 22;
+ star_clicked = cell_x < 25 && cell_y >= 30 && cell_y <= 48;
+ }
+
+ // Get the current conversation. If it's selected, we'll apply the mark operation to
+ // all selected conversations; otherwise, it just applies to this one.
+ Geary.App.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
+ Gee.Collection to_mark;
+ if (GearyApplication.instance.controller.get_selected_conversations().contains(conversation))
+ to_mark = GearyApplication.instance.controller.get_selected_conversations();
+ else
+ to_mark = new Geary.Collection.SingleItem(conversation);
+
+ if (read_clicked) {
+ // Read/unread.
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.UNREAD);
+
+ if (conversation.is_unread())
+ mark_conversations(to_mark, null, flags, false);
+ else
+ mark_conversations(to_mark, flags, null, true);
+
+ return true;
+ } else if (star_clicked) {
+ // Starred/unstarred.
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+ flags.add(Geary.EmailFlags.FLAGGED);
+
+ if (conversation.is_flagged())
+ mark_conversations(to_mark, null, flags, false);
+ else
+ mark_conversations(to_mark, flags, null, true);
+
+ return true;
}
- return true;
}
if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
diff --git a/src/client/views/conversation-web-view.vala b/src/client/views/conversation-web-view.vala
index 295f8dc6..47489357 100644
--- a/src/client/views/conversation-web-view.vala
+++ b/src/client/views/conversation-web-view.vala
@@ -142,12 +142,12 @@ public class ConversationWebView : WebKit.WebView {
assert(container != null);
// Load the icons.
- set_icon_src("#email_template .menu .icon", "go-down");
- set_icon_src("#email_template .starred .icon", "starred");
- set_icon_src("#email_template .unstarred .icon", "non-starred-grey");
- set_icon_src("#email_template .attachment.icon", "mail-attachment");
- set_icon_src("#email_template .close_show_images", "gtk-close");
- set_icon_src("#link_warning_template .close_link_warning", "gtk-close");
+ set_icon_src("#email_template .menu .icon", "go-down-symbolic");
+ set_icon_src("#email_template .starred .icon", "star-symbolic");
+ set_icon_src("#email_template .unstarred .icon", "unstarred-symbolic");
+ set_icon_src("#email_template .attachment.icon", "mail-attachment-symbolic");
+ set_icon_src("#email_template .close_show_images", "close-symbolic");
+ set_icon_src("#link_warning_template .close_link_warning", "close-symbolic");
}
private void load_user_style() {
@@ -201,20 +201,16 @@ public class ConversationWebView : WebKit.WebView {
private void set_icon_src(string selector, string icon_name) {
try {
- // Load the icon.
- string icon_filename = IconFactory.instance.lookup_icon(icon_name, 16).get_filename();
- uint8[] icon_content;
- FileUtils.get_data(icon_filename, out icon_content);
-
- // Fetch its mime type.
- bool uncertain_content_type;
- string icon_mimetype = ContentType.get_mime_type(ContentType.guess(icon_filename,
- icon_content, out uncertain_content_type));
+ // Load icon.
+ uint8[] icon_content = null;
+ Gdk.Pixbuf? pixbuf = IconFactory.instance.load_symbolic_colored(icon_name, 16);
+ if (pixbuf != null)
+ pixbuf.save_to_buffer(out icon_content, "png"); // Load as PNG.
// Then set the source to a data url.
WebKit.DOM.HTMLImageElement img = Util.DOM.select(get_dom_document(), selector)
as WebKit.DOM.HTMLImageElement;
- set_data_url(img, icon_mimetype, icon_content);
+ set_data_url(img, "image/png", icon_content);
} catch (Error error) {
warning("Failed to load icon '%s': %s", icon_name, error.message);
}
diff --git a/src/client/views/formatted-conversation-data.vala b/src/client/views/formatted-conversation-data.vala
index 9a0627b0..b41814ae 100644
--- a/src/client/views/formatted-conversation-data.vala
+++ b/src/client/views/formatted-conversation-data.vala
@@ -6,6 +6,8 @@
// Stores formatted data for a message.
public class FormattedConversationData : Geary.BaseObject {
+ public const string UNREAD_BG_COLOR = "#888888";
+
private const string ME = _("Me");
private const string STYLE_EXAMPLE = "Gg"; // Use both upper and lower case to get max height.
@@ -322,17 +324,21 @@ public class FormattedConversationData : Geary.BaseObject {
FormattedConversationData.preview_height = preview_height;
FormattedConversationData.cell_height = y + preview_height;
} else {
- // Flagged indicator.
- Gdk.Pixbuf icon = is_flagged ? IconFactory.instance.starred : IconFactory.instance.unstarred;
- Gdk.cairo_set_source_pixbuf(ctx, icon, cell_area.x + LINE_SPACING, cell_area.y + LINE_SPACING);
- ctx.paint();
+ int unread_y = GearyApplication.instance.config.display_preview ?
+ cell_area.y + LINE_SPACING * 2 : cell_area.y + LINE_SPACING;
// Unread indicator.
- if (is_unread) {
- Gdk.cairo_set_source_pixbuf(ctx, IconFactory.instance.unread, cell_area.x + LINE_SPACING,
- cell_area.y + (cell_area.height / 2) + LINE_SPACING);
- ctx.paint();
- }
+ Gdk.Pixbuf read_icon = is_unread ? IconFactory.instance.unread_colored
+ : IconFactory.instance.read_colored;
+ Gdk.cairo_set_source_pixbuf(ctx, read_icon, cell_area.x + LINE_SPACING, unread_y);
+ ctx.paint();
+
+ // Starred indicator.
+ Gdk.Pixbuf starred_icon = is_flagged ? IconFactory.instance.starred_colored
+ : IconFactory.instance.unstarred_colored;
+ Gdk.cairo_set_source_pixbuf(ctx, starred_icon, cell_area.x + LINE_SPACING, cell_area.y +
+ (cell_area.height / 2) + LINE_SPACING);
+ ctx.paint();
}
}
@@ -384,8 +390,8 @@ public class FormattedConversationData : Geary.BaseObject {
return 0;
string mails =
- " %d "
- .printf(FONT_SIZE_MESSAGE_COUNT, num_emails);
+ " %d "
+ .printf(UNREAD_BG_COLOR, FONT_SIZE_MESSAGE_COUNT, num_emails);
Pango.Layout layout_num = widget.create_pango_layout(null);
layout_num.set_markup(mails, -1);
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index ba753d99..f1a5644c 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -12,7 +12,6 @@ install(FILES message.glade DESTINATION ${UI_DEST})
install(FILES password-dialog.glade DESTINATION ${UI_DEST})
install(FILES preferences.glade DESTINATION ${UI_DEST})
install(FILES remove_confirm.glade DESTINATION ${UI_DEST})
-install(FILES toolbar.glade DESTINATION ${UI_DEST})
install(FILES toolbar_mark_menu.ui DESTINATION ${UI_DEST})
install(FILES toolbar_menu.ui DESTINATION ${UI_DEST})
install(FILES upgrade_dialog.glade DESTINATION ${UI_DEST})
diff --git a/ui/composer.glade b/ui/composer.glade
index 33fac03f..00886c86 100644
--- a/ui/composer.glade
+++ b/ui/composer.glade
@@ -59,8 +59,8 @@
@@ -72,28 +72,28 @@
@@ -110,25 +110,29 @@
@@ -142,11 +146,42 @@
-
-