add msvcr90.dll to windows installer. Fixes #6754
[gajim.git] / src / tooltips.py
blob6d193abe7ca225e128f8ac6c4409f4e786731f33
1 # -*- coding: utf-8 -*-
2 ## src/tooltips.py
3 ##
4 ## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
5 ## Stéphan Kochen <stephan AT kochen.nl>
6 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
7 ## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
8 ## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
9 ## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
10 ## Stefan Bethge <stefan AT lanpartei.de>
11 ## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
12 ## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
13 ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
14 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
16 ## This file is part of Gajim.
18 ## Gajim is free software; you can redistribute it and/or modify
19 ## it under the terms of the GNU General Public License as published
20 ## by the Free Software Foundation; version 3 only.
22 ## Gajim is distributed in the hope that it will be useful,
23 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ## GNU General Public License for more details.
27 ## You should have received a copy of the GNU General Public License
28 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
31 import gtk
32 import gobject
33 import os
34 import time
35 import locale
36 from datetime import datetime
37 from datetime import timedelta
39 import gtkgui_helpers
41 from common import gajim
42 from common import helpers
43 from common.pep import MOODS, ACTIVITIES
44 from common.i18n import Q_
46 class BaseTooltip:
47 """
48 Base Tooltip class
50 Usage:
51 tooltip = BaseTooltip()
52 ....
53 tooltip.show_tooltip(data, widget_height, widget_y_position)
54 ....
55 if tooltip.timeout != 0:
56 tooltip.hide_tooltip()
58 * data - the text to be displayed (extenders override this argument and
59 display more complex contents)
60 * widget_height - the height of the widget on which we want to show tooltip
61 * widget_y_position - the vertical position of the widget on the screen
63 Tooltip is displayed aligned centered to the mouse poiner and 4px below the widget.
64 In case tooltip goes below the visible area it is shown above the widget.
65 """
67 def __init__(self):
68 self.timeout = 0
69 self.preferred_position = [0, 0]
70 self.win = None
71 self.id = None
72 self.cur_data = None
73 self.check_last_time = None
75 def populate(self, data):
76 """
77 This method must be overriden by all extenders. This is the most simple
78 implementation: show data as value of a label
79 """
80 self.create_window()
81 self.win.add(gtk.Label(data))
83 def create_window(self):
84 """
85 Create a popup window each time tooltip is requested
86 """
87 self.win = gtk.Window(gtk.WINDOW_POPUP)
88 self.win.set_border_width(3)
89 self.win.set_resizable(False)
90 self.win.set_name('gtk-tooltips')
91 self.win.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLTIP)
93 self.win.set_events(gtk.gdk.POINTER_MOTION_MASK)
94 self.win.connect_after('expose_event', self.expose)
95 self.win.connect('size-request', self.on_size_request)
96 self.win.connect('motion-notify-event', self.motion_notify_event)
97 self.screen = self.win.get_screen()
99 def _get_icon_name_for_tooltip(self, contact):
101 Helper function used for tooltip contacts/acounts
103 Tooltip on account has fake contact with sub == '', in this case we show
104 real status of the account
106 if contact.ask == 'subscribe':
107 return 'requested'
108 elif contact.sub in ('both', 'to', ''):
109 return contact.show
110 return 'not in roster'
112 def motion_notify_event(self, widget, event):
113 self.hide_tooltip()
115 def on_size_request(self, widget, requisition):
116 half_width = requisition.width / 2 + 1
117 if self.preferred_position[0] < half_width:
118 self.preferred_position[0] = 0
119 elif self.preferred_position[0] + requisition.width > \
120 self.screen.get_width() + half_width:
121 self.preferred_position[0] = self.screen.get_width() - \
122 requisition.width
123 elif not self.check_last_time:
124 self.preferred_position[0] -= half_width
125 if self.preferred_position[1] + requisition.height > \
126 self.screen.get_height():
127 # flip tooltip up
128 self.preferred_position[1] -= requisition.height + \
129 self.widget_height + 8
130 if self.preferred_position[1] < 0:
131 self.preferred_position[1] = 0
132 self.win.move(self.preferred_position[0], self.preferred_position[1])
134 def expose(self, widget, event):
135 style = self.win.get_style()
136 size = self.win.get_size()
137 style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT,
138 None, self.win, 'tooltip', 0, 0, -1, 1)
139 style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT,
140 None, self.win, 'tooltip', 0, size[1] - 1, -1, 1)
141 style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT,
142 None, self.win, 'tooltip', 0, 0, 1, -1)
143 style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT,
144 None, self.win, 'tooltip', size[0] - 1, 0, 1, -1)
145 return True
147 def show_tooltip(self, data, widget_height, widget_y_position):
149 Show tooltip on widget
151 Data contains needed data for tooltip contents.
152 widget_height is the height of the widget on which we show the tooltip.
153 widget_y_position is vertical position of the widget on the screen.
155 self.cur_data = data
156 # set tooltip contents
157 self.populate(data)
159 # get the X position of mouse pointer on the screen
160 pointer_x = self.screen.get_display().get_pointer()[1]
162 # get the prefered X position of the tooltip on the screen in case this position is >
163 # than the height of the screen, tooltip will be shown above the widget
164 preferred_y = widget_y_position + widget_height + 4
166 self.preferred_position = [pointer_x, preferred_y]
167 self.widget_height = widget_height
168 self.win.ensure_style()
169 self.win.show_all()
171 def hide_tooltip(self):
172 if self.timeout > 0:
173 gobject.source_remove(self.timeout)
174 self.timeout = 0
175 if self.win:
176 self.win.destroy()
177 self.win = None
178 self.id = None
179 self.cur_data = None
180 self.check_last_time = None
182 @staticmethod
183 def colorize_status(status):
185 Colorize the status message inside the tooltip by it's
186 semantics. Color palette is the Tango.
188 formatted = "<span foreground='%s'>%s</span>"
189 if status.startswith(Q_("?user status:Available")):
190 status = formatted % ('#73D216', status)
191 elif status.startswith(_("Free for Chat")):
192 status = formatted % ('#3465A4', status)
193 elif status.startswith(_("Away")):
194 status = formatted % ('#EDD400', status)
195 elif status.startswith(_("Busy")):
196 status = formatted % ('#F57900', status)
197 elif status.startswith(_("Not Available")):
198 status = formatted % ('#CC0000', status)
199 elif status.startswith(_("Offline")):
200 status = formatted % ('#555753', status)
201 return status
203 @staticmethod
204 def colorize_affiliation(affiliation):
206 Color the affiliation of a MUC participant inside the tooltip by
207 it's semantics. Color palette is the Tango.
209 formatted = "<span foreground='%s'>%s</span>"
210 if affiliation.startswith(Q_("?Group Chat Contact Affiliation:None")):
211 affiliation = formatted % ('#555753', affiliation)
212 elif affiliation.startswith(_("Member")):
213 affiliation = formatted % ('#73D216', affiliation)
214 elif affiliation.startswith(_("Administrator")):
215 affiliation = formatted % ('#F57900', affiliation)
216 elif affiliation.startswith(_("Owner")):
217 affiliation = formatted % ('#CC0000', affiliation)
218 return affiliation
220 class StatusTable:
222 Contains methods for creating status table. This is used in Roster and
223 NotificationArea tooltips
226 def __init__(self):
227 self.current_row = 1
228 self.table = None
229 self.text_label = None
230 self.spacer_label = ' '
232 def create_table(self):
233 self.table = gtk.Table(4, 1)
234 self.table.set_property('column-spacing', 2)
236 def add_text_row(self, text, col_inc = 0):
237 self.current_row += 1
238 self.text_label = gtk.Label()
239 self.text_label.set_line_wrap(True)
240 self.text_label.set_alignment(0, 0)
241 self.text_label.set_selectable(False)
242 self.text_label.set_markup(text)
243 self.table.attach(self.text_label, 1 + col_inc, 4, self.current_row,
244 self.current_row + 1)
246 def get_status_info(self, resource, priority, show, status):
247 str_status = resource + ' (' + unicode(priority) + ')'
248 if status:
249 status = status.strip()
250 if status != '':
251 # make sure 'status' is unicode before we send to to reduce_chars
252 if isinstance(status, str):
253 status = unicode(status, encoding='utf-8')
254 # reduce to 100 chars, 1 line
255 status = helpers.reduce_chars_newlines(status, 100, 1)
256 str_status = gobject.markup_escape_text(str_status)
257 status = gobject.markup_escape_text(status)
258 str_status += ' - <i>' + status + '</i>'
259 return str_status
261 def add_status_row(self, file_path, show, str_status, status_time=None,
262 show_lock=False, indent=True):
264 Append a new row with status icon to the table
266 self.current_row += 1
267 state_file = show.replace(' ', '_')
268 files = []
269 files.append(os.path.join(file_path, state_file + '.png'))
270 files.append(os.path.join(file_path, state_file + '.gif'))
271 image = gtk.Image()
272 image.set_from_pixbuf(None)
273 for f in files:
274 if os.path.exists(f):
275 image.set_from_file(f)
276 break
277 spacer = gtk.Label(self.spacer_label)
278 image.set_alignment(1, 0.5)
279 if indent:
280 self.table.attach(spacer, 1, 2, self.current_row,
281 self.current_row + 1, 0, 0, 0, 0)
282 self.table.attach(image, 2, 3, self.current_row,
283 self.current_row + 1, gtk.FILL, gtk.FILL, 2, 0)
284 status_label = gtk.Label()
285 status_label.set_markup(str_status)
286 status_label.set_alignment(0, 0)
287 status_label.set_line_wrap(True)
288 self.table.attach(status_label, 3, 4, self.current_row,
289 self.current_row + 1, gtk.FILL | gtk.EXPAND, 0, 0, 0)
290 if show_lock:
291 lock_image = gtk.Image()
292 lock_image.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION,
293 gtk.ICON_SIZE_MENU)
294 self.table.attach(lock_image, 4, 5, self.current_row,
295 self.current_row + 1, 0, 0, 0, 0)
297 class NotificationAreaTooltip(BaseTooltip, StatusTable):
299 Tooltip that is shown in the notification area
302 def __init__(self):
303 BaseTooltip.__init__(self)
304 StatusTable.__init__(self)
306 def fill_table_with_accounts(self, accounts):
307 iconset = gajim.config.get('iconset')
308 if not iconset:
309 iconset = 'dcraven'
310 file_path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
311 for acct in accounts:
312 message = acct['message']
313 # before reducing the chars we should assure we send unicode, else
314 # there are possible pango TBs on 'set_markup'
315 if isinstance(message, str):
316 message = unicode(message, encoding = 'utf-8')
317 message = helpers.reduce_chars_newlines(message, 100, 1)
318 message = gobject.markup_escape_text(message)
319 if acct['name'] in gajim.con_types and \
320 gajim.con_types[acct['name']] in ('tls', 'ssl'):
321 show_lock = True
322 else:
323 show_lock = False
324 if message:
325 self.add_status_row(file_path, acct['show'],
326 gobject.markup_escape_text(acct['name']) + \
327 ' - ' + message, show_lock=show_lock, indent=False)
328 else:
329 self.add_status_row(file_path, acct['show'],
330 gobject.markup_escape_text(acct['name'])
331 , show_lock=show_lock, indent=False)
332 for line in acct['event_lines']:
333 self.add_text_row(' ' + line, 1)
335 def populate(self, data=''):
336 self.create_window()
337 self.create_table()
339 accounts = helpers.get_notification_icon_tooltip_dict()
340 self.table.resize(2, 1)
341 self.fill_table_with_accounts(accounts)
342 self.hbox = gtk.HBox()
343 self.table.set_property('column-spacing', 1)
345 self.hbox.add(self.table)
346 self.hbox.show_all()
348 class GCTooltip(BaseTooltip):
350 Tooltip that is shown in the GC treeview
353 def __init__(self):
354 self.account = None
355 self.text_label = gtk.Label()
356 self.text_label.set_line_wrap(True)
357 self.text_label.set_alignment(0, 0)
358 self.text_label.set_selectable(False)
359 self.avatar_image = gtk.Image()
361 BaseTooltip.__init__(self)
363 def populate(self, contact):
364 if not contact:
365 return
366 self.create_window()
367 vcard_table = gtk.Table(3, 1)
368 vcard_table.set_property('column-spacing', 2)
369 vcard_table.set_homogeneous(False)
370 vcard_current_row = 1
371 properties = []
373 nick_markup = '<b>' + \
374 gobject.markup_escape_text(contact.get_shown_name()) \
375 + '</b>'
376 properties.append((nick_markup, None))
378 if contact.status: # status message
379 status = contact.status.strip()
380 if status != '':
381 # escape markup entities
382 status = helpers.reduce_chars_newlines(status, 300, 5)
383 status = '<i>' +\
384 gobject.markup_escape_text(status) + '</i>'
385 properties.append((status, None))
387 show = helpers.get_uf_show(contact.show)
388 show = self.colorize_status(show)
389 properties.append((show, None))
391 if contact.jid.strip():
392 properties.append((_('Jabber ID: '), "<b>%s</b>" % contact.jid))
394 if hasattr(contact, 'resource') and contact.resource.strip():
395 properties.append((_('Resource: '),
396 gobject.markup_escape_text(contact.resource)))
398 if contact.affiliation != 'none':
399 uf_affiliation = helpers.get_uf_affiliation(contact.affiliation)
400 uf_affiliation =\
401 _('%(owner_or_admin_or_member)s of this group chat') %\
402 {'owner_or_admin_or_member': uf_affiliation}
403 uf_affiliation = self.colorize_affiliation(uf_affiliation)
404 properties.append((uf_affiliation, None))
406 # Add avatar
407 puny_name = helpers.sanitize_filename(contact.name)
408 puny_room = helpers.sanitize_filename(contact.room_jid)
409 file_ = helpers.get_avatar_path(os.path.join(gajim.AVATAR_PATH, puny_room,
410 puny_name))
411 if file_:
412 self.avatar_image.set_from_file(file_)
413 pix = self.avatar_image.get_pixbuf()
414 pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
415 self.avatar_image.set_from_pixbuf(pix)
416 else:
417 self.avatar_image.set_from_pixbuf(None)
418 while properties:
419 property_ = properties.pop(0)
420 vcard_current_row += 1
421 vertical_fill = gtk.FILL
422 if not properties:
423 vertical_fill |= gtk.EXPAND
424 label = gtk.Label()
425 label.set_alignment(0, 0)
426 if property_[1]:
427 label.set_markup(property_[0])
428 vcard_table.attach(label, 1, 2, vcard_current_row,
429 vcard_current_row + 1, gtk.FILL, vertical_fill, 0, 0)
430 label = gtk.Label()
431 label.set_alignment(0, 0)
432 label.set_markup(property_[1])
433 label.set_line_wrap(True)
434 vcard_table.attach(label, 2, 3, vcard_current_row,
435 vcard_current_row + 1, gtk.EXPAND | gtk.FILL,
436 vertical_fill, 0, 0)
437 else:
438 label.set_markup(property_[0])
439 label.set_line_wrap(True)
440 vcard_table.attach(label, 1, 3, vcard_current_row,
441 vcard_current_row + 1, gtk.FILL, vertical_fill, 0)
443 self.avatar_image.set_alignment(0, 0)
444 vcard_table.attach(self.avatar_image, 3, 4, 2, vcard_current_row + 1,
445 gtk.FILL, gtk.FILL | gtk.EXPAND, 3, 3)
446 self.win.add(vcard_table)
448 class RosterTooltip(NotificationAreaTooltip):
450 Tooltip that is shown in the roster treeview
453 def __init__(self):
454 self.account = None
455 self.image = gtk.Image()
456 self.image.set_alignment(0, 0)
457 # padding is independent of the total length and better than alignment
458 self.image.set_padding(1, 2)
459 self.avatar_image = gtk.Image()
460 NotificationAreaTooltip.__init__(self)
462 def populate(self, contacts):
463 self.create_window()
465 self.create_table()
466 if not contacts or len(contacts) == 0:
467 # Tooltip for merged accounts row
468 accounts = helpers.get_notification_icon_tooltip_dict()
469 self.table.resize(2, 1)
470 self.spacer_label = ''
471 self.fill_table_with_accounts(accounts)
472 self.win.add(self.table)
473 return
475 # primary contact
476 prim_contact = gajim.contacts.get_highest_prio_contact_from_contacts(
477 contacts)
479 puny_jid = helpers.sanitize_filename(prim_contact.jid)
480 table_size = 3
482 file_ = helpers.get_avatar_path(os.path.join(gajim.AVATAR_PATH, puny_jid))
483 if file_:
484 self.avatar_image.set_from_file(file_)
485 pix = self.avatar_image.get_pixbuf()
486 pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
487 self.avatar_image.set_from_pixbuf(pix)
488 table_size = 4
489 else:
490 self.avatar_image.set_from_pixbuf(None)
491 vcard_table = gtk.Table(table_size, 1)
492 vcard_table.set_property('column-spacing', 2)
493 vcard_table.set_homogeneous(False)
494 vcard_current_row = 1
495 properties = []
497 name_markup = u'<span weight="bold">' + \
498 gobject.markup_escape_text(prim_contact.get_shown_name())\
499 + '</span>'
500 if self.account and helpers.jid_is_blocked(self.account,
501 prim_contact.jid):
502 name_markup += _(' [blocked]')
503 if self.account and \
504 self.account in gajim.interface.minimized_controls and \
505 prim_contact.jid in gajim.interface.minimized_controls[self.account]:
506 name_markup += _(' [minimized]')
507 properties.append((name_markup, None))
509 num_resources = 0
510 # put contacts in dict, where key is priority
511 contacts_dict = {}
512 for contact in contacts:
513 if contact.resource:
514 num_resources += 1
515 if contact.priority in contacts_dict:
516 contacts_dict[contact.priority].append(contact)
517 else:
518 contacts_dict[contact.priority] = [contact]
520 if num_resources > 1:
521 properties.append((_('Status: '), ' '))
522 transport = gajim.get_transport_name_from_jid(
523 prim_contact.jid)
524 if transport:
525 file_path = os.path.join(helpers.get_transport_path(transport),
526 '16x16')
527 else:
528 iconset = gajim.config.get('iconset')
529 if not iconset:
530 iconset = 'dcraven'
531 file_path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
533 contact_keys = sorted(contacts_dict.keys())
534 contact_keys.reverse()
535 for priority in contact_keys:
536 for acontact in contacts_dict[priority]:
537 status_line = self.get_status_info(acontact.resource,
538 acontact.priority, acontact.show, acontact.status)
540 icon_name = self._get_icon_name_for_tooltip(acontact)
541 self.add_status_row(file_path, icon_name, status_line,
542 acontact.last_status_time)
543 properties.append((self.table, None))
545 else: # only one resource
546 if contact.show:
547 show = helpers.get_uf_show(contact.show)
548 if not self.check_last_time and self.account:
549 if contact.show == 'offline':
550 if not contact.last_status_time:
551 gajim.connections[self.account].request_last_status_time(
552 contact.jid, '')
553 else:
554 self.check_last_time = contact.last_status_time
555 elif contact.resource:
556 gajim.connections[self.account].request_last_status_time(
557 contact.jid, contact.resource)
558 if contact.last_activity_time:
559 self.check_last_time = contact.last_activity_time
560 else:
561 self.check_last_time = None
562 if contact.last_status_time:
563 vcard_current_row += 1
564 if contact.show == 'offline':
565 text = ' - ' + _('Last status: %s')
566 else:
567 text = _(' since %s')
569 if time.strftime('%j', time.localtime())== \
570 time.strftime('%j', contact.last_status_time):
571 # it's today, show only the locale hour representation
572 local_time = time.strftime('%X',
573 contact.last_status_time)
574 else:
575 # time.strftime returns locale encoded string
576 local_time = time.strftime('%c',
577 contact.last_status_time)
578 local_time = local_time.decode(
579 locale.getpreferredencoding())
580 text = text % local_time
581 show += text
582 if self.account and \
583 prim_contact.jid in gajim.gc_connected[self.account]:
584 if gajim.gc_connected[self.account][prim_contact.jid]:
585 show = _('Connected')
586 else:
587 show = _('Disconnected')
588 show = self.colorize_status(show)
590 if contact.status:
591 status = contact.status.strip()
592 if status:
593 # reduce long status
594 # (no more than 300 chars on line and no more than 5 lines)
595 # status is wrapped
596 status = helpers.reduce_chars_newlines(status, 300, 5)
597 # escape markup entities.
598 status = gobject.markup_escape_text(status)
599 properties.append(('<i>%s</i>' % status, None))
600 properties.append((show, None))
602 self._append_pep_info(contact, properties)
604 properties.append((_('Jabber ID: '), "<b>%s</b>" % prim_contact.jid))
606 # contact has only one ressource
607 if num_resources == 1 and contact.resource:
608 properties.append((_('Resource: '),
609 gobject.markup_escape_text(contact.resource) +\
610 ' (' + unicode(contact.priority) + ')'))
612 if self.account and prim_contact.sub and prim_contact.sub != 'both' and\
613 prim_contact.jid not in gajim.gc_connected[self.account]:
614 # ('both' is the normal sub so we don't show it)
615 properties.append(( _('Subscription: '),
616 gobject.markup_escape_text(helpers.get_uf_sub(prim_contact.sub))))
618 if prim_contact.keyID:
619 keyID = None
620 if len(prim_contact.keyID) == 8:
621 keyID = prim_contact.keyID
622 elif len(prim_contact.keyID) == 16:
623 keyID = prim_contact.keyID[8:]
624 if keyID:
625 properties.append((_('OpenPGP: '),
626 gobject.markup_escape_text(keyID)))
628 if contact.last_activity_time:
629 last_active = datetime(*contact.last_activity_time[:6])
630 current = datetime.now()
632 diff = current - last_active
633 diff = timedelta(diff.days, diff.seconds)
635 if last_active.date() == current.date():
636 formatted = last_active.strftime("%X")
637 else:
638 formatted = last_active.strftime("%c")
640 # Do not show the "Idle since" and "Idle for" items if there
641 # is no meaningful difference between last activity time and
642 # current time.
643 if diff.days > 0 or diff.seconds > 0:
644 cs = "<span foreground='#888A85'>%s</span>"
645 properties.append((str(), None))
646 properties.append(((cs % _("Idle since %s")) % formatted, None))
647 properties.append(((cs % _("Idle for %s")) % str(diff), None))
649 while properties:
650 property_ = properties.pop(0)
651 vcard_current_row += 1
652 vertical_fill = gtk.FILL
653 if not properties and table_size == 4:
654 vertical_fill |= gtk.EXPAND
655 label = gtk.Label()
656 label.set_alignment(0, 0)
657 if property_[1]:
658 label.set_markup(property_[0])
659 vcard_table.attach(label, 1, 2, vcard_current_row,
660 vcard_current_row + 1, gtk.FILL, vertical_fill, 0, 0)
661 label = gtk.Label()
662 label.set_alignment(0, 0)
663 label.set_markup(property_[1])
664 label.set_line_wrap(True)
665 vcard_table.attach(label, 2, 3, vcard_current_row,
666 vcard_current_row + 1, gtk.EXPAND | gtk.FILL,
667 vertical_fill, 0, 0)
668 else:
669 if isinstance(property_[0], (unicode, str)): # FIXME: rm unicode?
670 label.set_markup(property_[0])
671 label.set_line_wrap(True)
672 else:
673 label = property_[0]
674 vcard_table.attach(label, 1, 3, vcard_current_row,
675 vcard_current_row + 1, gtk.FILL, vertical_fill, 0)
676 self.avatar_image.set_alignment(0, 0)
677 if table_size == 4:
678 vcard_table.attach(self.avatar_image, 3, 4, 2,
679 vcard_current_row + 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 3, 3)
680 self.win.add(vcard_table)
682 def update_last_time(self, last_time):
683 if not self.check_last_time or time.strftime('%x %I:%M %p', last_time) !=\
684 time.strftime('%x %I:%M %p', self.check_last_time):
685 self.win.destroy()
686 self.win = None
687 self.populate(self.cur_data)
688 self.win.ensure_style()
689 self.win.show_all()
691 def _append_pep_info(self, contact, properties):
693 Append Tune, Mood, Activity, Location information of the specified contact
694 to the given property list.
696 if 'mood' in contact.pep:
697 mood = contact.pep['mood'].asMarkupText()
698 properties.append((_("Mood: %s") % mood, None))
700 if 'activity' in contact.pep:
701 activity = contact.pep['activity'].asMarkupText()
702 properties.append((_("Activity: %s") % activity, None))
704 if 'tune' in contact.pep:
705 tune = contact.pep['tune'].asMarkupText()
706 properties.append((_("Tune: %s") % tune, None))
708 if 'location' in contact.pep:
709 location = contact.pep['location'].asMarkupText()
710 properties.append((_("Location: %s") % location, None))
713 class FileTransfersTooltip(BaseTooltip):
715 Tooltip that is shown in the notification area
718 def __init__(self):
719 BaseTooltip.__init__(self)
721 def populate(self, file_props):
722 ft_table = gtk.Table(2, 1)
723 ft_table.set_property('column-spacing', 2)
724 current_row = 1
725 self.create_window()
726 properties = []
727 name = file_props['name']
728 if file_props['type'] == 'r':
729 file_name = os.path.split(file_props['file-name'])[1]
730 else:
731 file_name = file_props['name']
732 properties.append((_('Name: '),
733 gobject.markup_escape_text(file_name)))
734 if file_props['type'] == 'r':
735 type_ = _('Download')
736 actor = _('Sender: ')
737 sender = unicode(file_props['sender']).split('/')[0]
738 name = gajim.contacts.get_first_contact_from_jid(
739 file_props['tt_account'], sender).get_shown_name()
740 else:
741 type_ = _('Upload')
742 actor = _('Recipient: ')
743 receiver = file_props['receiver']
744 if hasattr(receiver, 'name'):
745 name = receiver.get_shown_name()
746 else:
747 name = receiver.split('/')[0]
748 properties.append((_('Type: '), type_))
749 properties.append((actor, gobject.markup_escape_text(name)))
751 transfered_len = file_props.get('received-len', 0)
752 properties.append((_('Transferred: '), helpers.convert_bytes(transfered_len)))
753 status = ''
754 if 'started' not in file_props or not file_props['started']:
755 status = _('Not started')
756 elif 'connected' in file_props:
757 if 'stopped' in file_props and \
758 file_props['stopped'] == True:
759 status = _('Stopped')
760 elif file_props['completed']:
761 status = _('Completed')
762 elif file_props['connected'] == False:
763 if file_props['completed']:
764 status = _('Completed')
765 else:
766 if 'paused' in file_props and \
767 file_props['paused'] == True:
768 status = _('?transfer status:Paused')
769 elif 'stalled' in file_props and \
770 file_props['stalled'] == True:
771 #stalled is not paused. it is like 'frozen' it stopped alone
772 status = _('Stalled')
773 else:
774 status = _('Transferring')
775 else:
776 status = _('Not started')
777 properties.append((_('Status: '), status))
778 if 'desc' in file_props:
779 file_desc = file_props['desc']
780 properties.append((_('Description: '), gobject.markup_escape_text(
781 file_desc)))
782 while properties:
783 property_ = properties.pop(0)
784 current_row += 1
785 label = gtk.Label()
786 label.set_alignment(0, 0)
787 label.set_markup(property_[0])
788 ft_table.attach(label, 1, 2, current_row, current_row + 1,
789 gtk.FILL, gtk.FILL, 0, 0)
790 label = gtk.Label()
791 label.set_alignment(0, 0)
792 label.set_line_wrap(True)
793 label.set_markup(property_[1])
794 ft_table.attach(label, 2, 3, current_row, current_row + 1,
795 gtk.EXPAND | gtk.FILL, gtk.FILL, 0, 0)
797 self.win.add(ft_table)
800 class ServiceDiscoveryTooltip(BaseTooltip):
802 Tooltip that is shown when hovering over a service discovery row
804 def populate(self, status):
805 self.create_window()
806 label = gtk.Label()
807 label.set_line_wrap(True)
808 label.set_alignment(0, 0)
809 label.set_selectable(False)
810 if status == 1:
811 label.set_text(
812 _('This service has not yet responded with detailed information'))
813 elif status == 2:
814 label.set_text(
815 _('This service could not respond with detailed information.\n'
816 'It is most likely legacy or broken'))
817 self.win.add(label)