From 7a278d91233e3ccf7d7e0462a2e6a0b2768d2f8e Mon Sep 17 00:00:00 2001 From: lovetox Date: Sun, 13 Nov 2016 21:06:37 +0100 Subject: [PATCH] Refactor GC Roster tooltip --- data/gui/tooltip_gc_contact.ui | 145 ++++++++++++++++++++++ src/groupchat_control.py | 107 +++++++--------- src/tooltips.py | 218 +++++++++++++++------------------ 3 files changed, 289 insertions(+), 181 deletions(-) create mode 100644 data/gui/tooltip_gc_contact.ui diff --git a/data/gui/tooltip_gc_contact.ui b/data/gui/tooltip_gc_contact.ui new file mode 100644 index 000000000..4af742fb1 --- /dev/null +++ b/data/gui/tooltip_gc_contact.ui @@ -0,0 +1,145 @@ + + + + + + True + False + 5 + + + True + False + start + 0 + + + + + + 0 + 0 + 2 + + + + + True + False + start + True + 40 + 0 + + + + + + 0 + 1 + 2 + + + + + True + False + start + Jabber ID: + 0 + + + 0 + 3 + + + + + True + False + gtk-missing-image + + + 2 + 0 + 7 + + + + + True + False + start + Resource: + 0 + + + 0 + 4 + + + + + True + False + start + 0 + + + 0 + 5 + 2 + + + + + True + False + start + 0 + + + 0 + 2 + 2 + + + + + True + False + True + + + 0 + 6 + 2 + + + + + True + False + 0 + + + 1 + 4 + + + + + True + False + 0 + + + + + + 1 + 3 + + + + diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 1e6781fad..aab9912c1 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -360,6 +360,12 @@ class GroupchatControl(ChatControlBase): img = self.xml.get_object('history_image') img.set_from_icon_name('document-open-recent', Gtk.IconSize.MENU) widget = self.xml.get_object('list_treeview') + widget.set_has_tooltip(True) + widget.set_tooltip_window(tooltips.GCTooltip(self.parent_win.window)) + self.current_tooltip = None + id_ = widget.connect('query-tooltip', self.query_tooltip) + self.handlers[id_] = widget + id_ = widget.connect('row_expanded', self.on_list_treeview_row_expanded) self.handlers[id_] = widget @@ -379,14 +385,6 @@ class GroupchatControl(ChatControlBase): self.on_list_treeview_key_press_event) self.handlers[id_] = widget - id_ = widget.connect('motion_notify_event', - self.on_list_treeview_motion_notify_event) - self.handlers[id_] = widget - - id_ = widget.connect('leave_notify_event', - self.on_list_treeview_leave_notify_event) - self.handlers[id_] = widget - self.room_jid = self.contact.jid self.nick = contact.name self.new_nick = '' @@ -419,8 +417,6 @@ class GroupchatControl(ChatControlBase): self.subject = '' - self.tooltip = tooltips.GCTooltip() - # nickname coloring self.gc_count_nicknames_colors = -1 self.gc_custom_colors = {} @@ -530,6 +526,46 @@ class GroupchatControl(ChatControlBase): # instance object gajim.plugin_manager.gui_extension_point('groupchat_control', self) + def query_tooltip(self, widget, x_pos, y_pos, keyboard_mode, tooltip): + try: + row = self.list_treeview.get_path_at_pos(x_pos, y_pos)[0] + except TypeError: + return False + if not row: + return False + + iter_ = None + try: + iter_ = self.model.get_iter(row) + except Exception: + return False + + typ = self.model[iter_][C_TYPE] + nick = self.model[iter_][C_NICK] + + if typ != 'contact': + return False + + if self.current_tooltip != row: + # If the row changes we hide the current tooltip + self.current_tooltip = row + return False + + tooltip = widget.get_tooltip_window() + + if tooltip.row == row: + # We already populated the window with the row data + return True + tooltip.row = row + + contact = gajim.contacts.get_gc_contact( + self.account, self.room_jid, nick) + if not contact: + return False + + tooltip.populate(contact) + return True + def fill_column(self, col): for rend in self.renderers_list: col.pack_start(rend[1], rend[2]) @@ -2638,8 +2674,6 @@ class GroupchatControl(ChatControlBase): """ Popup user's group's or agent menu """ - # hide tooltip, no matter the button is pressed - self.tooltip.hide_tooltip() try: pos = widget.get_path_at_pos(int(event.x), int(event.y)) path, x = pos[0], pos[2] @@ -2697,55 +2731,6 @@ class GroupchatControl(ChatControlBase): add = gc_refer_to_nick_char + ' ' message_buffer.insert_at_cursor(start + nick + add) - def on_list_treeview_motion_notify_event(self, widget, event): - props = widget.get_path_at_pos(int(event.x), int(event.y)) - if self.tooltip.timeout > 0 or self.tooltip.shown: - if not props or self.tooltip.id != props[0]: - self.tooltip.hide_tooltip() - if props: - [row, col, x, y] = props - iter_ = None - try: - iter_ = self.model.get_iter(row) - except Exception: - self.tooltip.hide_tooltip() - return - typ = self.model[iter_][C_TYPE] - if typ == 'contact': - account = self.account - - if self.tooltip.timeout == 0 or self.tooltip.id != props[0]: - self.tooltip.id = row - nick = self.model[iter_][C_NICK] - self.tooltip.timeout = GLib.timeout_add(500, - self.show_tooltip, gajim.contacts.get_gc_contact( - account, self.room_jid, nick)) - - def on_list_treeview_leave_notify_event(self, widget, event): - props = widget.get_path_at_pos(int(event.x), int(event.y)) - if self.tooltip.timeout > 0 or self.tooltip.shown: - if not props or self.tooltip.id == props[0]: - self.tooltip.hide_tooltip() - - def show_tooltip(self, contact): - self.tooltip.timeout = 0 - if not self.list_treeview.get_window(): - # control has been destroyed since tooltip was requested - return - w = self.list_treeview.get_window() - device = w.get_display().get_device_manager().get_client_pointer() - pointer = w.get_device_position(device) - props = self.list_treeview.get_path_at_pos(pointer[1], pointer[2]) - # check if the current pointer is at the same path - # as it was before setting the timeout - if props and self.tooltip.id == props[0]: - rect = self.list_treeview.get_cell_area(props[0], props[1]) - position = w.get_origin()[1:] - self.tooltip.show_tooltip(contact, rect.height, - position[1] + rect.y) - else: - self.tooltip.hide_tooltip() - def grant_voice(self, widget, nick): """ Grant voice privilege to a user diff --git a/src/tooltips.py b/src/tooltips.py index c39d551f8..2391557cf 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -188,50 +188,6 @@ class BaseTooltip: self.check_last_time = None self.shown = False - @staticmethod - def colorize_status(status): - """ - Colorize the status message inside the tooltip by it's - semantics. Color palette is the Tango. - """ - formatted = "%s" - color = None - if status.startswith(Q_("?user status:Available")): - color = gajim.config.get('tooltip_status_online_color') - elif status.startswith(_("Free for Chat")): - color = gajim.config.get('tooltip_status_free_for_chat_color') - elif status.startswith(_("Away")): - color = gajim.config.get('tooltip_status_away_color') - elif status.startswith(_("Busy")): - color = gajim.config.get('tooltip_status_busy_color') - elif status.startswith(_("Not Available")): - color = gajim.config.get('tooltip_status_na_color') - elif status.startswith(_("Offline")): - color = gajim.config.get('tooltip_status_offline_color') - if color: - status = formatted % (color, status) - return status - - @staticmethod - def colorize_affiliation(affiliation): - """ - Color the affiliation of a MUC participant inside the tooltip by - it's semantics. Color palette is the Tango. - """ - formatted = "%s" - color = None - if affiliation.startswith(Q_("?Group Chat Contact Affiliation:None")): - color = gajim.config.get('tooltip_affiliation_none_color') - elif affiliation.startswith(_("Member")): - color = gajim.config.get('tooltip_affiliation_member_color') - elif affiliation.startswith(_("Administrator")): - color = gajim.config.get('tooltip_affiliation_administrator_color') - elif affiliation.startswith(_("Owner")): - color = gajim.config.get('tooltip_affiliation_owner_color') - if color: - affiliation = formatted % (color, affiliation) - return affiliation - class StatusTable: """ Contains methods for creating status table. This is used in Roster and @@ -356,68 +312,76 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): self.hbox.add(self.table) self.hbox.show_all() -class GCTooltip(BaseTooltip): - """ - Tooltip that is shown in the GC treeview - """ +class GCTooltip(Gtk.Window): + # pylint: disable=E1101 + def __init__(self, parent): + super().__init__(self, type=Gtk.WindowType.POPUP, transient_for=parent) + self.row = None + self.set_title('tooltip') + self.set_border_width(3) + self.set_resizable(False) + self.set_name('gtk-tooltips') + self.set_type_hint(Gdk.WindowTypeHint.TOOLTIP) - def __init__(self): - self.account = None - self.text_label = Gtk.Label() - self.text_label.set_line_wrap(True) - self.text_label.set_halign(Gtk.Align.START) - self.text_label.set_valign(Gtk.Align.START) - self.text_label.set_selectable(False) - self.avatar_image = Gtk.Image() + self.xml = gtkgui_helpers.get_gtk_builder('tooltip_gc_contact.ui') + for name in ('nick', 'status', 'jid', 'user_show', 'fillelement', + 'resource', 'affiliation', 'avatar', 'resource_label', + 'jid_label', 'tooltip_grid'): + setattr(self, name, self.xml.get_object(name)) - BaseTooltip.__init__(self) + self.add(self.tooltip_grid) + self.tooltip_grid.show() + + def clear_tooltip(self): + """ + Hide all Elements of the Tooltip Grid + """ + for child in self.tooltip_grid.get_children(): + child.hide() def populate(self, contact): - if not contact: - return - self.create_window() - vcard_table = Gtk.Grid() - vcard_table.insert_row(0) - vcard_table.insert_row(0) - vcard_table.insert_row(0) - vcard_table.insert_column(0) - vcard_table.set_property('column-spacing', 2) - vcard_current_row = 1 - properties = [] + """ + Populate the Tooltip Grid with data of from the contact + """ + self.clear_tooltip() - nick_markup = '' + GLib.markup_escape_text(contact.get_shown_name())\ - + '' - properties.append((nick_markup, None)) + self.nick.set_text(contact.get_shown_name()) + self.nick.show() - if contact.status: # status message + # Status Message + if contact.status: status = contact.status.strip() if status != '': - # escape markup entities - status = helpers.reduce_chars_newlines(status, 300, 5) - status = '' + GLib.markup_escape_text(status) + '' - properties.append((status, None)) + self.status.set_text(status) + self.status.show() + # Status show = helpers.get_uf_show(contact.show) - show = self.colorize_status(show) - properties.append((show, None)) + self.user_show.set_markup(colorize_status(show)) + self.user_show.show() + # JID if contact.jid.strip(): - properties.append((_('Jabber ID: '), '\u200E' + "%s" % \ - contact.jid)) - + self.jid.set_text(contact.jid) + self.jid.show() + self.jid_label.show() + # Resource if hasattr(contact, 'resource') and contact.resource.strip(): - properties.append((_('Resource: '), GLib.markup_escape_text( - contact.resource))) + self.resource.set_text(contact.resource) + self.resource.show() + self.resource_label.show() + # Affiliation if contact.affiliation != 'none': uf_affiliation = helpers.get_uf_affiliation(contact.affiliation) uf_affiliation = \ - _('%(owner_or_admin_or_member)s of this group chat') % \ - {'owner_or_admin_or_member': uf_affiliation} + _('%(owner_or_admin_or_member)s of this group chat') \ + % {'owner_or_admin_or_member': uf_affiliation} uf_affiliation = self.colorize_affiliation(uf_affiliation) - properties.append((uf_affiliation, None)) + self.affiliation.set_markup(uf_affiliation) + self.affiliation.show() - # Add avatar + # Avatar puny_name = helpers.sanitize_filename(contact.name) puny_room = helpers.sanitize_filename(contact.room_jid) file_ = helpers.get_avatar_path(os.path.join(gajim.AVATAR_PATH, @@ -426,39 +390,29 @@ class GCTooltip(BaseTooltip): with open(file_, 'rb') as file_data: pix = gtkgui_helpers.get_pixbuf_from_data(file_data.read()) pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip') - self.avatar_image.set_from_pixbuf(pix) - else: - self.avatar_image.set_from_pixbuf(None) - while properties: - property_ = properties.pop(0) - vcard_current_row += 1 - label = Gtk.Label() - if not properties: - label.set_vexpand(True) - label.set_halign(Gtk.Align.START) - label.set_valign(Gtk.Align.START) - if property_[1]: - label.set_markup(property_[0]) - vcard_table.attach(label, 1, vcard_current_row, 1, 1) - label = Gtk.Label() - if not properties: - label.set_vexpand(True) - label.set_halign(Gtk.Align.START) - label.set_valign(Gtk.Align.START) - label.set_markup(property_[1]) - label.set_line_wrap(True) - vcard_table.attach(label, 2, vcard_current_row, 1, 1) - else: - label.set_markup(property_[0]) - label.set_line_wrap(True) - vcard_table.attach(label, 1, vcard_current_row, 2, 1) + self.avatar.set_from_pixbuf(pix) + self.avatar.show() + self.fillelement.show() - self.avatar_image.set_halign(Gtk.Align.START) - self.avatar_image.set_valign(Gtk.Align.START) - vcard_table.attach(self.avatar_image, 3, 2, 1, vcard_current_row - 1) - gajim.plugin_manager.gui_extension_point('gc_tooltip_populate', - self, contact, vcard_table) - self.win.add(vcard_table) + @staticmethod + def colorize_affiliation(affiliation): + """ + Color the affiliation of a MUC participant inside the tooltip by + it's semantics. Color palette is the Tango. + """ + formatted = "%s" + color = None + if affiliation.startswith(Q_("?Group Chat Contact Affiliation:None")): + color = gajim.config.get('tooltip_affiliation_none_color') + elif affiliation.startswith(_("Member")): + color = gajim.config.get('tooltip_affiliation_member_color') + elif affiliation.startswith(_("Administrator")): + color = gajim.config.get('tooltip_affiliation_administrator_color') + elif affiliation.startswith(_("Owner")): + color = gajim.config.get('tooltip_affiliation_owner_color') + if color: + affiliation = formatted % (color, affiliation) + return affiliation class RosterTooltip(NotificationAreaTooltip): """ @@ -602,7 +556,7 @@ class RosterTooltip(NotificationAreaTooltip): show = _('Connected') else: show = _('Disconnected') - show = self.colorize_status(show) + show = colorize_status(show) if contact.status: status = contact.status.strip() @@ -816,3 +770,27 @@ class FileTransfersTooltip(BaseTooltip): Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0) self.win.add(ft_table) + + +def colorize_status(status): + """ + Colorize the status message inside the tooltip by it's + semantics. Color palette is the Tango. + """ + formatted = "%s" + color = None + if status.startswith(Q_("?user status:Available")): + color = gajim.config.get('tooltip_status_online_color') + elif status.startswith(_("Free for Chat")): + color = gajim.config.get('tooltip_status_free_for_chat_color') + elif status.startswith(_("Away")): + color = gajim.config.get('tooltip_status_away_color') + elif status.startswith(_("Busy")): + color = gajim.config.get('tooltip_status_busy_color') + elif status.startswith(_("Not Available")): + color = gajim.config.get('tooltip_status_na_color') + elif status.startswith(_("Offline")): + color = gajim.config.get('tooltip_status_offline_color') + if color: + status = formatted % (color, status) + return status \ No newline at end of file