tooltips.py 21.8 KB
Newer Older
Philipp Hörist's avatar
Philipp Hörist committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
#                    Stéphan Kochen <stephan AT kochen.nl>
# Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
# Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
# Copyright (C) 2005-2014 Yann Leboulanger <asterix AT lagaule.org>
# Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
#                    Stefan Bethge <stefan AT lanpartei.de>
# Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
# Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
# Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
26

Philipp Hörist's avatar
Philipp Hörist committed
27 28
import os
import time
Philipp Hörist's avatar
Philipp Hörist committed
29
import logging
Philipp Hörist's avatar
Philipp Hörist committed
30 31
from datetime import datetime

32
from gi.repository import Gtk
Yann Leboulanger's avatar
Yann Leboulanger committed
33
from gi.repository import GLib
34
from gi.repository import Pango
35

36
from gajim.common import app
André's avatar
André committed
37
from gajim.common import helpers
Philipp Hörist's avatar
Philipp Hörist committed
38
from gajim.common.const import AvatarSize
39
from gajim.common.const import PEPEventType
André's avatar
André committed
40
from gajim.common.i18n import Q_
41
from gajim.common.i18n import _
Daniel Brötzmann's avatar
Daniel Brötzmann committed
42
from gajim.gtkgui_helpers import add_css_class
43

Philipp Hörist's avatar
Philipp Hörist committed
44
from gajim.gtk.util import get_builder
45
from gajim.gtk.util import get_icon_name
46
from gajim.gtk.util import format_mood
Philipp Hörist's avatar
Philipp Hörist committed
47
from gajim.gtk.util import format_activity
Philipp Hörist's avatar
Philipp Hörist committed
48
from gajim.gtk.util import format_tune
Philipp Hörist's avatar
Philipp Hörist committed
49
from gajim.gtk.util import format_location
Daniel Brötzmann's avatar
Daniel Brötzmann committed
50
from gajim.gtk.util import get_css_show_class
Philipp Hörist's avatar
Philipp Hörist committed
51

Philipp Hörist's avatar
Philipp Hörist committed
52

53
log = logging.getLogger('gajim.gtk.tooltips')
Philipp Hörist's avatar
Philipp Hörist committed
54

55 56

class StatusTable:
57 58 59 60 61 62
    """
    Contains methods for creating status table. This is used in Roster and
    NotificationArea tooltips
    """

    def __init__(self):
lovetox's avatar
lovetox committed
63
        self.current_row = 0
64 65 66 67 68
        self.table = None
        self.text_label = None
        self.spacer_label = '   '

    def create_table(self):
69 70
        self.table = Gtk.Grid()
        self.table.insert_column(0)
71
        self.table.set_property('column-spacing', 3)
72

lovetox's avatar
lovetox committed
73 74
    def add_text_row(self, text, col_inc=0):
        self.table.insert_row(self.current_row)
75
        self.text_label = Gtk.Label()
76
        self.text_label.set_line_wrap(True)
77
        self.text_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
78 79 80
        self.text_label.set_lines(3)
        self.text_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.text_label.set_max_width_chars(30)
81 82
        self.text_label.set_halign(Gtk.Align.START)
        self.text_label.set_valign(Gtk.Align.START)
83
        self.text_label.set_xalign(0)
84
        self.text_label.set_selectable(False)
85 86

        self.text_label.set_text(text)
Philipp Hörist's avatar
Philipp Hörist committed
87 88 89 90
        self.table.attach(self.text_label, 1 + col_inc,
                          self.current_row,
                          3 - col_inc,
                          1)
lovetox's avatar
lovetox committed
91
        self.current_row += 1
92

93 94
    def add_status_row(self, show, str_status, show_lock=False,
                       indent=True, transport=None):
95 96 97
        """
        Append a new row with status icon to the table
        """
lovetox's avatar
lovetox committed
98
        self.table.insert_row(self.current_row)
99
        image = Gtk.Image()
100
        icon_name = get_icon_name(show, transport=transport)
101
        image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
102
        spacer = Gtk.Label(label=self.spacer_label)
103
        image.set_halign(Gtk.Align.START)
104
        image.set_valign(Gtk.Align.CENTER)
105
        if indent:
106 107
            self.table.attach(spacer, 1, self.current_row, 1, 1)
        self.table.attach(image, 2, self.current_row, 1, 1)
108
        status_label = Gtk.Label()
109
        status_label.set_text(str_status)
110 111
        status_label.set_halign(Gtk.Align.START)
        status_label.set_valign(Gtk.Align.START)
112
        status_label.set_xalign(0)
113
        status_label.set_line_wrap(True)
114 115 116 117
        status_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
        status_label.set_lines(3)
        status_label.set_ellipsize(Pango.EllipsizeMode.END)
        status_label.set_max_width_chars(30)
118
        self.table.attach(status_label, 3, self.current_row, 1, 1)
119
        if show_lock:
120
            lock_image = Gtk.Image()
Philipp Hörist's avatar
Philipp Hörist committed
121
            lock_image.set_from_icon_name('dialog-password', Gtk.IconSize.MENU)
122
            self.table.attach(lock_image, 4, self.current_row, 1, 1)
lovetox's avatar
lovetox committed
123
        self.current_row += 1
124

125 126 127 128
    def fill_table_with_accounts(self, accounts):
        for acct in accounts:
            message = acct['message']
            message = helpers.reduce_chars_newlines(message, 100, 1)
Yann Leboulanger's avatar
Yann Leboulanger committed
129
            message = GLib.markup_escape_text(message)
130 131 132
            con_type = app.con_types.get(acct['name'])
            show_lock = con_type in ('tls', 'ssl')

Philipp Hörist's avatar
Philipp Hörist committed
133
            account_label = GLib.markup_escape_text(acct['account_label'])
134
            if message:
Philipp Hörist's avatar
Philipp Hörist committed
135
                status = '%s - %s' % (account_label, message)
136
            else:
Philipp Hörist's avatar
Philipp Hörist committed
137 138
                status = account_label

139
            self.add_status_row(acct['show'],
Philipp Hörist's avatar
Philipp Hörist committed
140 141 142 143
                                status,
                                show_lock=show_lock,
                                indent=False)

144 145 146
            for line in acct['event_lines']:
                self.add_text_row('  ' + line, 1)

Philipp Hörist's avatar
Philipp Hörist committed
147 148 149 150 151 152 153 154 155

class NotificationAreaTooltip(StatusTable):
    """
    Tooltip that is shown in the notification area
    """

    def __init__(self):
        StatusTable.__init__(self)

156
    def get_tooltip(self):
157 158 159 160 161 162
        self.create_table()

        accounts = helpers.get_notification_icon_tooltip_dict()
        self.fill_table_with_accounts(accounts)
        self.table.set_property('column-spacing', 1)

Philipp Hörist's avatar
Philipp Hörist committed
163 164 165 166
        hbox = Gtk.HBox()
        hbox.add(self.table)
        hbox.show_all()
        return hbox
167

nkour's avatar
nkour committed
168

Philipp Hörist's avatar
Philipp Hörist committed
169 170 171
class GCTooltip():
    def __init__(self):
        self.contact = None
lovetox's avatar
lovetox committed
172

Philipp Hörist's avatar
Philipp Hörist committed
173
        self._ui = get_builder('tooltip_gc_contact.ui')
lovetox's avatar
lovetox committed
174 175

    def clear_tooltip(self):
Philipp Hörist's avatar
Philipp Hörist committed
176 177 178 179
        self.contact = None

    def get_tooltip(self, contact):
        if self.contact == contact:
Philipp Hörist's avatar
Philipp Hörist committed
180
            return True, self._ui.tooltip_grid
Philipp Hörist's avatar
Philipp Hörist committed
181 182 183

        self._populate_grid(contact)
        self.contact = contact
Philipp Hörist's avatar
Philipp Hörist committed
184
        return False, self._ui.tooltip_grid
Philipp Hörist's avatar
Philipp Hörist committed
185 186

    def _hide_grid_childs(self):
lovetox's avatar
lovetox committed
187 188 189
        """
        Hide all Elements of the Tooltip Grid
        """
Philipp Hörist's avatar
Philipp Hörist committed
190
        for child in self._ui.tooltip_grid.get_children():
lovetox's avatar
lovetox committed
191
            child.hide()
192

Philipp Hörist's avatar
Philipp Hörist committed
193
    def _populate_grid(self, contact):
lovetox's avatar
lovetox committed
194 195 196
        """
        Populate the Tooltip Grid with data of from the contact
        """
Philipp Hörist's avatar
Philipp Hörist committed
197
        self._hide_grid_childs()
198

Philipp Hörist's avatar
Philipp Hörist committed
199 200
        self._ui.nick.set_text(contact.get_shown_name())
        self._ui.nick.show()
201

lovetox's avatar
lovetox committed
202 203
        # Status Message
        if contact.status:
204 205
            status = contact.status.strip()
            if status != '':
Philipp Hörist's avatar
Philipp Hörist committed
206 207
                self._ui.status.set_text(status)
                self._ui.status.show()
208

lovetox's avatar
lovetox committed
209
        # Status
Philipp Hörist's avatar
Philipp Hörist committed
210
        show = helpers.get_uf_show(contact.show.value)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
211
        self._ui.user_show.set_text(show)
212
        colorize_status(self._ui.user_show, contact.show.value)
Philipp Hörist's avatar
Philipp Hörist committed
213
        self._ui.user_show.show()
214

lovetox's avatar
lovetox committed
215
        # JID
Philipp Hörist's avatar
Philipp Hörist committed
216 217
        if contact.jid is not None:
            self._ui.jid.set_text(str(contact.jid))
Philipp Hörist's avatar
Philipp Hörist committed
218
            self._ui.jid.show()
219

lovetox's avatar
lovetox committed
220
        # Affiliation
Philipp Hörist's avatar
Philipp Hörist committed
221
        if not contact.affiliation.is_none:
Philipp Hörist's avatar
Philipp Hörist committed
222
            uf_affiliation = helpers.get_uf_affiliation(contact.affiliation)
223
            uf_affiliation = \
lovetox's avatar
lovetox committed
224 225
                _('%(owner_or_admin_or_member)s of this group chat') \
                % {'owner_or_admin_or_member': uf_affiliation}
226
            self._ui.affiliation.set_text(uf_affiliation)
Philipp Hörist's avatar
Philipp Hörist committed
227
            self._ui.affiliation.show()
228

lovetox's avatar
lovetox committed
229
        # Avatar
Philipp Hörist's avatar
Philipp Hörist committed
230 231 232
        if contact.avatar_sha is not None:
            app.log('avatar').debug(
                'Load GCTooltip: %s %s', contact.name, contact.avatar_sha)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
233 234 235 236 237 238
        scale = self._ui.tooltip_grid.get_scale_factor()
        surface = app.interface.get_avatar(
            contact, AvatarSize.TOOLTIP, scale)
        self._ui.avatar.set_from_surface(surface)
        self._ui.avatar.show()
        self._ui.fillelement.show()
239

240
        app.plugin_manager.gui_extension_point(
Philipp Hörist's avatar
Philipp Hörist committed
241
            'gc_tooltip_populate', self, contact, self._ui.tooltip_grid)
242

Philipp Hörist's avatar
Philipp Hörist committed
243 244 245
    def destroy(self):
        self._ui.tooltip_grid.destroy()

246

247 248
class RosterTooltip(StatusTable):
    def __init__(self):
lovetox's avatar
lovetox committed
249 250
        StatusTable.__init__(self)
        self.create_table()
Philipp Hörist's avatar
Philipp Hörist committed
251
        self.account = None
lovetox's avatar
lovetox committed
252 253
        self.row = None
        self.contact_jid = None
Philipp Hörist's avatar
Philipp Hörist committed
254
        self.prim_contact = None
lovetox's avatar
lovetox committed
255 256
        self.last_widget = None
        self.num_resources = 0
257

Philipp Hörist's avatar
Philipp Hörist committed
258
        self._ui = get_builder('tooltip_roster_contact.ui')
259

lovetox's avatar
lovetox committed
260 261 262 263
    def clear_tooltip(self):
        """
        Hide all Elements of the Tooltip Grid
        """
Philipp Hörist's avatar
Philipp Hörist committed
264
        for child in self._ui.tooltip_grid.get_children():
lovetox's avatar
lovetox committed
265
            child.hide()
266
        status_table = self._ui.tooltip_grid.get_child_at(1, 3)
lovetox's avatar
lovetox committed
267 268 269
        if status_table:
            status_table.destroy()
            self.create_table()
270 271 272 273 274 275 276 277 278
        self.row = None

    def get_tooltip(self, row, connected_contacts, account, typ):
        if self.row == row:
            return True, self._ui.tooltip_grid

        self._populate_grid(connected_contacts, account, typ)
        self.row = row
        return False, self._ui.tooltip_grid
lovetox's avatar
lovetox committed
279

280
    def _populate_grid(self, contacts, account, typ):
lovetox's avatar
lovetox committed
281 282 283 284 285 286 287 288 289 290 291
        """
        Populate the Tooltip Grid with data of from the contact
        """
        self.current_row = 0
        self.account = account
        if self.last_widget:
            self.last_widget.set_vexpand(False)

        self.clear_tooltip()

        if account == 'all':
292
            # Tooltip for merged accounts row
Philipp Hörist's avatar
Philipp Hörist committed
293
            self._show_merged_account_tooltip()
294 295
            return

lovetox's avatar
lovetox committed
296
        if typ == 'account':
297
            jid = app.get_jid_from_account(account)
lovetox's avatar
lovetox committed
298
            contacts = []
299
            connection = app.connections[account]
lovetox's avatar
lovetox committed
300 301
            # get our current contact info

302
            nbr_on, nbr_total = app.\
Philipp Hörist's avatar
Philipp Hörist committed
303
                contacts.get_nb_online_total_contacts(accounts=[account])
304
            account_name = app.get_account_label(account)
305
            if app.account_is_available(account):
Philipp Hörist's avatar
Philipp Hörist committed
306 307 308 309 310
                account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
            contact = app.contacts.create_self_contact(
                jid=jid,
                account=account,
                name=account_name,
Philipp Hörist's avatar
Philipp Hörist committed
311 312
                show=connection.status,
                status=connection.status_message,
313
                resource=connection.get_own_jid().getResource(),
lovetox's avatar
lovetox committed
314
                priority=connection.priority)
Philipp Hörist's avatar
Philipp Hörist committed
315

lovetox's avatar
lovetox committed
316 317 318
            contacts.append(contact)

        # Username/Account/Groupchat
319
        self.prim_contact = app.contacts.get_highest_prio_contact_from_contacts(
320
            contacts)
Philipp Hörist's avatar
Philipp Hörist committed
321 322
        if self.prim_contact is None:
            log.error('No contact for Roster tooltip found')
Philipp Hörist's avatar
Philipp Hörist committed
323 324
            log.error('contacts: %s, typ: %s, account: %s',
                      contacts, typ, account)
Philipp Hörist's avatar
Philipp Hörist committed
325
            return
lovetox's avatar
lovetox committed
326 327
        self.contact_jid = self.prim_contact.jid
        name = GLib.markup_escape_text(self.prim_contact.get_shown_name())
328

329
        if app.config.get('mergeaccounts'):
330
            name = GLib.markup_escape_text(
Philipp Hörist's avatar
Philipp Hörist committed
331
                self.prim_contact.account.name)
332

333
        self._ui.name.set_markup(name)
Philipp Hörist's avatar
Philipp Hörist committed
334
        self._ui.name.show()
lovetox's avatar
lovetox committed
335 336

        self.num_resources = 0
337 338 339 340
        # put contacts in dict, where key is priority
        contacts_dict = {}
        for contact in contacts:
            if contact.resource:
lovetox's avatar
lovetox committed
341 342 343 344
                self.num_resources += 1
                priority = int(contact.priority)
                if priority in contacts_dict:
                    contacts_dict[priority].append(contact)
345
                else:
lovetox's avatar
lovetox committed
346 347
                    contacts_dict[priority] = [contact]
        if self.num_resources > 1:
348
            transport = app.get_transport_name_from_jid(self.prim_contact.jid)
349 350
            if transport == 'jabber':
                transport = None
351 352 353 354
            contact_keys = sorted(contacts_dict.keys())
            contact_keys.reverse()
            for priority in contact_keys:
                for acontact in contacts_dict[priority]:
355
                    show = self._get_icon_name_for_tooltip(acontact)
356 357 358 359 360 361 362
                    status = acontact.status
                    resource_line = '%s (%s)' % (acontact.resource,
                                                 str(acontact.priority))
                    self.add_status_row(
                        show, resource_line, transport=transport)
                    if status:
                        self.add_text_row(status, 2)
lovetox's avatar
lovetox committed
363

364
            self._ui.tooltip_grid.attach(self.table, 1, 3, 2, 1)
lovetox's avatar
lovetox committed
365
            self.table.show_all()
366

lovetox's avatar
lovetox committed
367
        else:  # only one resource
368 369 370 371 372 373 374 375
            if contact.is_groupchat:
                disco_info = app.logger.get_last_disco_info(contact.jid)
                if disco_info is not None:
                    description = disco_info.muc_description
                    if description:
                        self._ui.status.set_text(description)
                        self._ui.status.show()
            elif contact.show and contact.status:
376 377
                status = contact.status.strip()
                if status:
Philipp Hörist's avatar
Philipp Hörist committed
378 379
                    self._ui.status.set_text(status)
                    self._ui.status.show()
380

lovetox's avatar
lovetox committed
381 382
        # PEP Info
        self._append_pep_info(contact)
383

lovetox's avatar
lovetox committed
384
        # JID
Philipp Hörist's avatar
Philipp Hörist committed
385 386
        self._ui.jid.set_text(self.prim_contact.jid)
        self._ui.jid.show()
387

Alexander Krotov's avatar
Alexander Krotov committed
388
        # contact has only one resource
lovetox's avatar
lovetox committed
389 390 391
        if self.num_resources == 1 and contact.resource:
            res = GLib.markup_escape_text(contact.resource)
            prio = str(contact.priority)
Philipp Hörist's avatar
Philipp Hörist committed
392 393 394
            self._ui.resource.set_text("{} ({})".format(res, prio))
            self._ui.resource.show()
            self._ui.resource_label.show()
395

396
        if self.prim_contact.jid not in app.gc_connected[account]:
lovetox's avatar
lovetox committed
397
            if (account and
Philipp Hörist's avatar
Philipp Hörist committed
398
                    self.prim_contact.sub and
lovetox's avatar
lovetox committed
399 400
                    self.prim_contact.sub != 'both'):
                # ('both' is the normal sub so we don't show it)
Philipp Hörist's avatar
Philipp Hörist committed
401 402 403
                self._ui.sub.set_text(helpers.get_uf_sub(self.prim_contact.sub))
                self._ui.sub.show()
                self._ui.sub_label.show()
lovetox's avatar
lovetox committed
404 405 406 407

        self._set_idle_time(contact)

        # Avatar
408
        scale = self._ui.tooltip_grid.get_scale_factor()
409 410
        surface = app.contacts.get_avatar(
            account, self.prim_contact.jid, AvatarSize.TOOLTIP, scale)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
411 412
        self._ui.avatar.set_from_surface(surface)
        self._ui.avatar.show()
Philipp Hörist's avatar
Philipp Hörist committed
413

414
        app.plugin_manager.gui_extension_point(
Philipp Hörist's avatar
Philipp Hörist committed
415
            'roster_tooltip_populate', self, contacts, self._ui.tooltip_grid)
416

Philipp Hörist's avatar
Philipp Hörist committed
417
        # Sets the Widget that is at the bottom to expand.
418
        # This is needed in case the Picture takes more Space than the Labels
Philipp Hörist's avatar
Philipp Hörist committed
419 420
        i = 1
        while i < 15:
421 422 423
            if self._ui.tooltip_grid.get_child_at(1, i):
                if self._ui.tooltip_grid.get_child_at(1, i).get_visible():
                    self.last_widget = self._ui.tooltip_grid.get_child_at(1, i)
Philipp Hörist's avatar
Philipp Hörist committed
424 425
            i += 1
        self.last_widget.set_vexpand(True)
lovetox's avatar
lovetox committed
426

Philipp Hörist's avatar
Philipp Hörist committed
427 428 429 430 431 432 433
    def _show_merged_account_tooltip(self):
        accounts = helpers.get_notification_icon_tooltip_dict()
        self.spacer_label = ''
        self.fill_table_with_accounts(accounts)
        self._ui.tooltip_grid.attach(self.table, 1, 3, 2, 1)
        self.table.show_all()

lovetox's avatar
lovetox committed
434 435
    def _append_pep_info(self, contact):
        """
Philipp Hörist's avatar
Philipp Hörist committed
436 437
        Append Tune, Mood, Activity, Location information of the
        specified contact to the given property list.
lovetox's avatar
lovetox committed
438
        """
439 440
        if PEPEventType.MOOD in contact.pep:
            mood = format_mood(*contact.pep[PEPEventType.MOOD])
Philipp Hörist's avatar
Philipp Hörist committed
441 442 443
            self._ui.mood.set_markup(mood)
            self._ui.mood.show()
            self._ui.mood_label.show()
lovetox's avatar
lovetox committed
444

Philipp Hörist's avatar
Philipp Hörist committed
445 446
        if PEPEventType.ACTIVITY in contact.pep:
            activity = format_activity(*contact.pep[PEPEventType.ACTIVITY])
Philipp Hörist's avatar
Philipp Hörist committed
447 448 449
            self._ui.activity.set_markup(activity)
            self._ui.activity.show()
            self._ui.activity_label.show()
lovetox's avatar
lovetox committed
450

Philipp Hörist's avatar
Philipp Hörist committed
451 452
        if PEPEventType.TUNE in contact.pep:
            tune = format_tune(*contact.pep[PEPEventType.TUNE])
Philipp Hörist's avatar
Philipp Hörist committed
453 454 455
            self._ui.tune.set_markup(tune)
            self._ui.tune.show()
            self._ui.tune_label.show()
456

Philipp Hörist's avatar
Philipp Hörist committed
457 458
        if PEPEventType.LOCATION in contact.pep:
            location = format_location(contact.pep[PEPEventType.LOCATION])
Philipp Hörist's avatar
Philipp Hörist committed
459 460 461
            self._ui.location.set_markup(location)
            self._ui.location.show()
            self._ui.location_label.show()
lovetox's avatar
lovetox committed
462 463

    def _set_idle_time(self, contact):
464 465 466 467
        if contact.idle_time:
            idle_time = contact.idle_time
            idle_time = time.localtime(contact.idle_time)
            idle_time = datetime(*(idle_time[:6]))
468
            current = datetime.now()
469
            if idle_time.date() == current.date():
Philipp Hörist's avatar
Philipp Hörist committed
470
                formatted = idle_time.strftime('%X')
471
            else:
Philipp Hörist's avatar
Philipp Hörist committed
472
                formatted = idle_time.strftime('%c')
473
            self._ui.idle_since.set_text(formatted)
Philipp Hörist's avatar
Philipp Hörist committed
474 475
            self._ui.idle_since.show()
            self._ui.idle_since_label.show()
lovetox's avatar
lovetox committed
476 477 478 479 480

        if contact.show and self.num_resources < 2:
            show = helpers.get_uf_show(contact.show)
            # Contact is Groupchat
            if (self.account and
481 482
                    self.prim_contact.jid in app.gc_connected[self.account]):
                if app.gc_connected[self.account][self.prim_contact.jid]:
lovetox's avatar
lovetox committed
483 484 485
                    show = _('Connected')
                else:
                    show = _('Disconnected')
486

Daniel Brötzmann's avatar
Daniel Brötzmann committed
487 488
            colorize_status(self._ui.user_show, contact.show)
            self._ui.user_show.set_text(show)
Philipp Hörist's avatar
Philipp Hörist committed
489
            self._ui.user_show.show()
lovetox's avatar
lovetox committed
490

Philipp Hörist's avatar
Philipp Hörist committed
491 492
    @staticmethod
    def _get_icon_name_for_tooltip(contact):
lovetox's avatar
lovetox committed
493
        """
Alexander Krotov's avatar
Alexander Krotov committed
494
        Helper function used for tooltip contacts/accounts
lovetox's avatar
lovetox committed
495 496 497 498 499 500

        Tooltip on account has fake contact with sub == '', in this case we show
        real status of the account
        """
        if contact.ask == 'subscribe':
            return 'requested'
501
        if contact.sub in ('both', 'to', ''):
lovetox's avatar
lovetox committed
502 503 504
            return contact.show
        return 'not in roster'

505

506
class FileTransfersTooltip():
507
    def __init__(self):
508 509
        self.sid = None
        self.widget = None
510 511 512 513
        if app.config.get('use_kib_mib'):
            self.units = GLib.FormatSizeFlags.IEC_UNITS
        else:
            self.units = GLib.FormatSizeFlags.DEFAULT
514

515 516 517 518 519 520 521 522 523 524 525 526
    def clear_tooltip(self):
        self.sid = None
        self.widget = None

    def get_tooltip(self, file_props, sid):
        if self.sid == sid:
            return True, self.widget

        self.widget = self._create_tooltip(file_props, sid)
        self.sid = sid
        return False, self.widget

Philipp Hörist's avatar
Philipp Hörist committed
527
    def _create_tooltip(self, file_props, _sid):
528 529 530 531 532
        ft_grid = Gtk.Grid.new()
        ft_grid.insert_column(0)
        ft_grid.set_row_spacing(6)
        ft_grid.set_column_spacing(12)
        current_row = 0
533
        properties = []
zimio's avatar
zimio committed
534 535 536
        name = file_props.name
        if file_props.type_ == 'r':
            file_name = os.path.split(file_props.file_name)[1]
537
        else:
zimio's avatar
zimio committed
538
            file_name = file_props.name
539 540
        properties.append((_('File Name: '),
                           GLib.markup_escape_text(file_name)))
zimio's avatar
zimio committed
541
        if file_props.type_ == 'r':
Yann Leboulanger's avatar
Yann Leboulanger committed
542
            type_ = Q_('?Noun:Download')
543
            actor = _('Sender: ')
Yann Leboulanger's avatar
Yann Leboulanger committed
544
            sender = file_props.sender.split('/')[0]
545
            name = app.contacts.get_first_contact_from_jid(
Philipp Hörist's avatar
Philipp Hörist committed
546
                file_props.tt_account, sender).get_shown_name()
547
        else:
Yann Leboulanger's avatar
Yann Leboulanger committed
548
            type_ = Q_('?Noun:Upload')
549
            actor = _('Recipient: ')
zimio's avatar
zimio committed
550
            receiver = file_props.receiver
551 552 553 554
            if hasattr(receiver, 'name'):
                name = receiver.get_shown_name()
            else:
                name = receiver.split('/')[0]
555
        properties.append((Q_('?transfer type:Type: '), type_))
Yann Leboulanger's avatar
Yann Leboulanger committed
556
        properties.append((actor, GLib.markup_escape_text(name)))
557

zimio's avatar
zimio committed
558 559 560
        transfered_len = file_props.received_len
        if not transfered_len:
            transfered_len = 0
561
        properties.append((Q_('?transfer status:Transferred: '),
562
                           GLib.format_size_full(transfered_len, self.units)))
Philipp Hörist's avatar
Philipp Hörist committed
563
        status = self._get_current_status(file_props)
564
        properties.append((Q_('?transfer status:Status: '), status))
565
        file_desc = file_props.desc or ''
566 567 568
        properties.append((_('Description: '),
                           GLib.markup_escape_text(file_desc)))

569 570
        while properties:
            property_ = properties.pop(0)
571
            label = Gtk.Label()
572 573
            label.set_halign(Gtk.Align.END)
            label.set_valign(Gtk.Align.CENTER)
574
            label.set_markup(property_[0])
575
            ft_grid.attach(label, 0, current_row, 1, 1)
576
            label = Gtk.Label()
577 578
            label.set_halign(Gtk.Align.START)
            label.set_valign(Gtk.Align.START)
579 580
            label.set_line_wrap(True)
            label.set_markup(property_[1])
581 582
            ft_grid.attach(label, 1, current_row, 1, 1)
            current_row += 1
583

584 585
        ft_grid.show_all()
        return ft_grid
lovetox's avatar
lovetox committed
586

Philipp Hörist's avatar
Philipp Hörist committed
587 588 589
    @staticmethod
    def _get_current_status(file_props):
        if file_props.stopped:
590
            return Q_('?transfer status:Aborted')
Philipp Hörist's avatar
Philipp Hörist committed
591
        if file_props.completed:
592
            return Q_('?transfer status:Completed')
Philipp Hörist's avatar
Philipp Hörist committed
593 594 595 596
        if file_props.paused:
            return Q_('?transfer status:Paused')
        if file_props.stalled:
            # stalled is not paused. it is like 'frozen' it stopped alone
597
            return Q_('?transfer status:Stalled')
Philipp Hörist's avatar
Philipp Hörist committed
598 599 600

        if file_props.connected:
            if file_props.started:
601 602 603
                return Q_('?transfer status:Transferring')
            return Q_('?transfer status:Not started')
        return Q_('?transfer status:Not started')
Philipp Hörist's avatar
Philipp Hörist committed
604

lovetox's avatar
lovetox committed
605

Daniel Brötzmann's avatar
Daniel Brötzmann committed
606
def colorize_status(widget, show):
lovetox's avatar
lovetox committed
607
    """
Daniel Brötzmann's avatar
Daniel Brötzmann committed
608
    Colorize the status message inside the tooltip by it's semantics.
lovetox's avatar
lovetox committed
609
    """
Daniel Brötzmann's avatar
Daniel Brötzmann committed
610 611
    css_class = get_css_show_class(show)[14:]
    add_css_class(widget, css_class, prefix='gajim-status-')