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 @@
+
+
+
+
+
+
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