using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Util; namespace Dino.Ui { public class AvatarImage : Misc { public int height { get; set; default = 35; } public int width { get; set; default = 35; } public bool allow_gray { get; set; default = true; } public StreamInteractor? stream_interactor { get; set; } public AvatarManager? avatar_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(AvatarManager.IDENTITY); } } public MucManager? muc_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(MucManager.IDENTITY); } } public PresenceManager? presence_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(PresenceManager.IDENTITY); } } public ConnectionManager? connection_manager { owned get { return stream_interactor == null ? null : stream_interactor.connection_manager; } } public Account account { get { return conversation.account; } } private AvatarDrawer? drawer; private Conversation conversation; private Jid[] jids; private Cairo.ImageSurface? cached_surface; private static int8 use_image_surface = -1; public AvatarImage() { can_focus = false; get_style_context().add_class("avatar"); } public override void get_preferred_width(out int minimum_width, out int natural_width) { minimum_width = width; natural_width = width; } public override void get_preferred_height(out int minimum_height, out int natural_height) { minimum_height = height; natural_height = height; } public override bool draw(Cairo.Context ctx_in) { if (drawer == null) return false; Cairo.Context ctx = ctx_in; int width = this.width, height = this.height, base_factor = 1; if (use_image_surface == -1) { // TODO: detect if we have to buffer in image surface use_image_surface = 1; } if (use_image_surface == 1) { ctx_in.scale(1f/scale_factor, 1f/scale_factor); if (cached_surface != null) { ctx_in.set_source_surface(cached_surface, 0, 0); ctx_in.paint(); return true; } width *= scale_factor; height *= scale_factor; base_factor *= scale_factor; cached_surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height); ctx = new Cairo.Context(cached_surface); } drawer.size(height, width) .scale(base_factor) .font(get_pango_context().get_font_description().get_family()) .draw_on_context(ctx); if (use_image_surface == 1) { ctx_in.set_source_surface(ctx.get_target(), 0, 0); ctx_in.paint(); } return true; } public override void destroy() { disconnect_stream_interactor(); } private void disconnect_stream_interactor() { if (stream_interactor != null) { presence_manager.show_received.disconnect(on_show_received); avatar_manager.received_avatar.disconnect(on_received_avatar); stream_interactor.connection_manager.connection_state_changed.disconnect(on_connection_changed); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.disconnect(on_roster_updated); muc_manager.private_room_occupant_updated.disconnect(on_private_room_occupant_updated); muc_manager.room_info_updated.disconnect(on_room_info_updated); stream_interactor = null; } } private void on_show_received(Show show, Jid jid, Account account) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void on_received_avatar(Gdk.Pixbuf avatar, Jid jid, Account account) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void update_avatar_if_jid(Jid jid) { if (jid.equals_bare(this.conversation.counterpart)) { update_avatar_async.begin(); return; } foreach (Jid ours in this.jids) { if (jid.equals_bare(ours)) { update_avatar_async.begin(); return; } } } private void on_connection_changed(Account account, ConnectionManager.ConnectionState state) { if (!account.equals(this.account)) return; update_avatar_async.begin(); } private void on_roster_updated(Account account, Jid jid, Roster.Item roster_item) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void on_private_room_occupant_updated(Account account, Jid room, Jid occupant) { if (!account.equals(this.account)) return; update_avatar_if_jid(room); } private void on_room_info_updated(Account account, Jid muc_jid) { if (!account.equals(this.account)) return; update_avatar_if_jid(muc_jid); } private bool is_self_online() { if (connection_manager != null) { return connection_manager.get_state(account) == ConnectionManager.ConnectionState.CONNECTED; } return false; } private bool is_counterpart_online() { return presence_manager.get_full_jids(conversation.counterpart, account) != null; } public void set_conversation(StreamInteractor stream_interactor, Conversation conversation) { set_avatar_async.begin(stream_interactor, conversation, new Jid[0]); } public void set_conversation_participant(StreamInteractor stream_interactor, Conversation conversation, Jid sub_jid) { set_avatar_async.begin(stream_interactor, conversation, new Jid[] {sub_jid}); } public void set_conversation_participants(StreamInteractor stream_interactor, Conversation conversation, Jid[] sub_jids) { set_avatar_async.begin(stream_interactor, conversation, sub_jids); } private async void update_avatar_async() { this.cached_surface = null; this.drawer = yield Util.get_conversation_participants_avatar_drawer(stream_interactor, conversation, jids); if (allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); queue_draw(); } private async void set_avatar_async(StreamInteractor stream_interactor, Conversation conversation, Jid[] jids) { if (this.stream_interactor != null && stream_interactor != this.stream_interactor) { disconnect_stream_interactor(); } if (this.stream_interactor != stream_interactor) { this.stream_interactor = stream_interactor; presence_manager.show_received.connect(on_show_received); stream_interactor.get_module(AvatarManager.IDENTITY).received_avatar.connect(on_received_avatar); stream_interactor.connection_manager.connection_state_changed.connect(on_connection_changed); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect(on_roster_updated); muc_manager.private_room_occupant_updated.connect(on_private_room_occupant_updated); muc_manager.room_info_updated.connect(on_room_info_updated); } this.cached_surface = null; this.conversation = conversation; this.jids = jids; yield update_avatar_async(); } public void set_text(string text, bool gray = true) { disconnect_stream_interactor(); this.drawer = new AvatarDrawer().tile(null, text, null); if (gray) drawer.grayscale(); queue_draw(); } } }