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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + + + +image/svg+xml \ 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 @@ + + + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + Gnome Symbolic Icon Theme + + + + + 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 @@ + +image/svg+xml \ 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 @@ + + + +image/svg+xml \ 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - 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 @@ + + + +image/svg+xml \ 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 @@ + + + +image/svg+xml \ 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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + +image/svg+xml \ 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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + 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 @@ + +image/svg+xml \ 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 @@ - Lin_k - insert-link + Link (Ctrl+L) + insert-link-symbolic @@ -72,28 +72,28 @@ - Menu - go-down + More options + go-down-symbolic - Quote text - format-indent-more + Quote text (Ctrl+]) + format-indent-more-symbolic - Unquote text - format-indent-less + Unquote text (Ctrl+[) + format-indent-less-symbolic - Remove formatting - format-text-none + Remove formatting (Ctrl+Space) + format-text-remove-symbolic @@ -110,25 +110,29 @@ - format-text-bold + Bold (Ctrl+B) + format-text-bold-symbolic - format-text-italic + Italic (Ctrl+I) + format-text-italic-symbolic - format-text-underline + Underline (Ctrl+U) + format-text-underline-symbolic - format-text-strikethrough + Strikethrough (Ctrl+K) + format-text-strikethrough-symbolic @@ -142,11 +146,42 @@ - - - True - False - down + + + Large + Large + + + + + Medium + Medium + + + + + Small + Small + + + + + Sans Serif + Sans Serif + + + + + Serif + Serif + + + + + Fixed width + Fixed width + + True @@ -415,170 +450,11 @@ - + True False - icons - 2 - - bold - True - False - Bold (Ctrl+B) - Bold - True - - - False - True - - - - - italic - True - False - Italic (Ctrl+I) - Italic - True - - - False - True - - - - - underline - True - False - Underline (Ctrl+U) - Underline - True - - - False - True - - - - - strikethrough - True - False - Strikethrough (Ctrl+K) - Strikethrough - True - - - False - True - - - - - True - False - - - False - True - - - - - indent - True - False - Quote text (Ctrl+]) - Quote text - True - - - False - True - - - - - outdent - True - False - Unquote text (Ctrl+[) - Unquote text - True - - - False - True - - - - - True - False - - - False - True - - - - - insertlink - True - False - Link (Ctrl+L) - Link - True - - - False - True - - - - - removeformat - True - False - Remove formatting (Ctrl+Space) - Remove formatting - True - - - False - True - - - - - True - False - - - - - - True - - - - - False - menu - True - False - Formatting Menu - Menu - True - menu arrow - - - False - True - + diff --git a/ui/composer_accelerators.ui b/ui/composer_accelerators.ui index eaa3022b..86205871 100644 --- a/ui/composer_accelerators.ui +++ b/ui/composer_accelerators.ui @@ -27,5 +27,13 @@ + + + + + + + + diff --git a/ui/toolbar.glade b/ui/toolbar.glade deleted file mode 100644 index 9b79290a..00000000 --- a/ui/toolbar.glade +++ /dev/null @@ -1,192 +0,0 @@ - - - - - True - False - - - True - False - Start new conversation (Ctrl+N, N) - True - New Message - True - mail-message-new - - - False - True - - - - - True - False - - - False - - - - - True - False - Reply to last message in conversation (Ctrl+R, R) - True - Reply - True - mail-reply-sender - - - False - True - - - - - True - False - Reply to everyone in last message of conversation (Ctrl+Shift+R, Shift+R) - True - Reply All - True - mail-reply-all - - - False - True - - - - - True - False - Send copy of last message in conversation (Ctrl+L, F) - True - Forward - True - mail-forward - - - False - True - - - - - True - False - - - False - - - - - True - False - Mark - edit-mark - - - False - True - - - - - True - False - Label as - tag-new - - - False - True - - - - - True - False - Move to - mail-move - - - False - True - - - - - True - False - - - False - - - - - True - False - True - Delete - - - False - True - - - - - True - False - True - - - - - - True - - - - - True - False - True - - - True - True - True - - 32 - edit-find-symbolic - edit-clear-symbolic - False - - - - - False - True - - - - - True - False - Menu - True - application-menu - - - False - True - - - - diff --git a/ui/toolbar_mark_menu.ui b/ui/toolbar_mark_menu.ui index e4ca6d66..662b27b2 100644 --- a/ui/toolbar_mark_menu.ui +++ b/ui/toolbar_mark_menu.ui @@ -1,8 +1,4 @@ - @@ -10,18 +6,5 @@ - - - - - - - - - diff --git a/ui/toolbar_menu.ui b/ui/toolbar_menu.ui index cdc86a9e..da354b8c 100644 --- a/ui/toolbar_menu.ui +++ b/ui/toolbar_menu.ui @@ -1,7 +1,4 @@ - @@ -11,19 +8,4 @@ - - - - - - - - - - -