root/branches/gajim_0.8.2/src/tooltips.py

Revision 3244, 15.1 kB (checked in by dkirov, 3 years ago)

fixed image align pb in long tooltips

Line 
1##      tooltips.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##      - Nikos Kouremenos <kourem@gmail.com>
7##      - Dimitur Kirov <dkirov@gmail.com>
8##
9##      Copyright (C) 2003-2005 Gajim Team
10##
11## This program is free software; you can redistribute it and/or modify
12## it under the terms of the GNU General Public License as published
13## by the Free Software Foundation; version 2 only.
14##
15## This program is distributed in the hope that it will be useful,
16## but WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20
21import gtk
22import gobject
23import os
24
25import gtkgui_helpers
26
27from common import gajim
28from common import helpers
29from common import i18n
30
31_ = i18n._
32APP = i18n.APP
33
34class BaseTooltip:
35        ''' Base Tooltip . Usage:
36                tooltip = BaseTooltip()
37                ....
38                tooltip.show_tooltip('', window_postions, widget_postions)
39                ....
40                if tooltip.timeout != 0:
41                        tooltip.hide_tooltip()
42        '''
43        def __init__(self):
44                self.timeout = 0
45                self.prefered_position = [0, 0]
46                self.win = None
47                self.id = None
48               
49        def populate(self, data):
50                ''' this method must be overriden by all extenders '''
51                self.create_window()
52                self.win.add(gtk.Label(data))
53               
54        def create_window(self):
55                ''' create a popup window each time tooltip is requested '''
56                self.win = gtk.Window(gtk.WINDOW_POPUP)
57                self.win.set_border_width(3)
58                self.win.set_resizable(False)
59                self.win.set_name('gtk-tooltips')
60               
61               
62                self.win.set_events(gtk.gdk.POINTER_MOTION_MASK)
63                self.win.connect_after('expose_event', self.expose)
64                self.win.connect('size-request', self.size_request)
65                self.win.connect('motion-notify-event', self.motion_notify_event)
66       
67        def motion_notify_event(self, widget, event):
68                self.hide_tooltip()
69
70        def size_request(self, widget, requisition):
71                screen = self.win.get_screen()
72                half_width = requisition.width / 2 + 1
73                if self.prefered_position[0] < half_width:
74                        self.prefered_position[0] = 0
75                elif self.prefered_position[0]  + requisition.width > screen.get_width() \
76                                + half_width:
77                        self.prefered_position[0] = screen.get_width() - requisition.width
78                else:
79                        self.prefered_position[0] -= half_width
80                        screen.get_height()
81                if self.prefered_position[1] + requisition.height > screen.get_height():
82                        # flip tooltip up
83                        self.prefered_position[1] -= requisition.height  + self.widget_height + 8
84                if self.prefered_position[1] < 0:
85                        self.prefered_position[1] = 0
86                self.win.move(self.prefered_position[0], self.prefered_position[1])
87
88        def expose(self, widget, event):
89                style = self.win.get_style()
90                size = self.win.get_size()
91                style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None,
92                        self.win, 'tooltip', 0, 0, -1, 1)
93                style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None,
94                        self.win, 'tooltip', 0, size[1] - 1, -1, 1)
95                style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None,
96                        self.win, 'tooltip', 0, 0, 1, -1)
97                style.paint_flat_box(self.win.window, gtk.STATE_NORMAL, gtk.SHADOW_OUT, None,
98                        self.win, 'tooltip', size[0] - 1, 0, 1, -1)
99                return True
100       
101        def show_tooltip(self, data, widget_pos, win_size):
102                self.populate(data)
103                new_x = win_size[0] + widget_pos[0] 
104                new_y = win_size[1] + widget_pos[1] + 4
105                self.prefered_position = [new_x, new_y]
106                self.widget_height = widget_pos[1]
107                self.win.ensure_style()
108                self.win.show_all()
109
110        def hide_tooltip(self):
111                if(self.timeout > 0):
112                        gobject.source_remove(self.timeout)
113                        self.timeout = 0
114                if self.win:
115                        self.win.destroy()
116                        self.win = None
117                self.id = None
118
119class StatusTable:
120        ''' Contains methods for creating status table. This
121        is used in Roster and NotificationArea tooltips '''
122        def __init__(self):
123                self.current_row = 1
124                self.table = None
125                self.text_lable = None
126               
127        def create_table(self):
128                self.table = gtk.Table(3, 1)
129                self.table.set_property('column-spacing', 2)
130                self.text_lable = gtk.Label()
131                self.text_lable.set_line_wrap(True)
132                self.text_lable.set_alignment(0, 0)
133                self.text_lable.set_selectable(False)
134                self.table.attach(self.text_lable, 1, 4, 1, 2)
135               
136        def get_status_info(self, resource, priority, show, status):
137                str_status = resource + ' (' + unicode(priority) + ')'
138                if status:
139                        status = status.strip()
140                        if status != '':
141                                # make sure 'status' is unicode before we send to to reduce_chars...
142                                if type(status) == str:
143                                        status = unicode(status, encoding='utf-8')
144                                if gtk.gtk_version < (2, 6, 0) or gtk.pygtk_version < (2, 6, 0):
145                                        status = gtkgui_helpers.reduce_chars_newlines(status, 50, 1)
146                                else:
147                                        status = gtkgui_helpers.reduce_chars_newlines(status, 0, 1)
148                                str_status += ' - ' + status
149                return gtkgui_helpers.escape_for_pango_markup(str_status)
150       
151        def add_status_row(self, file_path, show, str_status):
152                ''' appends a new row with status icon to the table '''
153                self.current_row += 1
154                state_file = show.replace(' ', '_')
155                files = []
156                files.append(os.path.join(file_path, state_file + '.png'))
157                files.append(os.path.join(file_path, state_file + '.gif'))
158                image = gtk.Image()
159                image.set_from_pixbuf(None)
160                spacer = gtk.Label('   ')
161                for file in files:
162                        if os.path.exists(file):
163                                image.set_from_file(file)
164                                break
165                image.set_alignment(0., 1.)
166                self.table.attach(spacer, 1, 2, self.current_row, 
167                        self.current_row + 1, 0, 0, 0, 0)
168                self.table.attach(image, 2, 3, self.current_row, 
169                        self.current_row + 1, 0, 0, 3, 0)
170                status_label = gtk.Label()
171                status_label.set_markup(str_status)
172                status_label.set_alignment(0., 0.)
173                self.table.attach(status_label, 3, 4, self.current_row,
174                        self.current_row + 1, gtk.EXPAND | gtk.FILL, 0, 0, 0)
175       
176class NotificationAreaTooltip(BaseTooltip, StatusTable):
177        ''' Tooltip that is shown in the notification area '''
178        def __init__(self, plugin):
179                self.plugin = plugin
180                BaseTooltip.__init__(self)
181                StatusTable.__init__(self)
182
183        def populate(self, data):
184                self.create_window()
185                self.create_table()
186                self.hbox = gtk.HBox()
187                self.table.set_property('column-spacing', 1)
188                text, single_line, accounts = '', '', []
189                if gajim.contacts:
190                        for account in gajim.contacts.keys():
191                                status_idx = gajim.connections[account].connected
192                                # uncomment the following to hide offline accounts
193                                # if status_idx == 0: continue
194                                status = gajim.SHOW_LIST[status_idx]
195                                message = gajim.connections[account].status
196                                single_line = helpers.get_uf_show(status)
197                                if message is None:
198                                        message = ''
199                                else:
200                                        message = message.strip()
201                                if message != '':
202                                        single_line += ': ' + message
203                                # the other solution is to hide offline accounts
204                                elif status == 'offline':
205                                        message = helpers.get_uf_show(status)
206                                accounts.append({'name': account, 'status_line': single_line, 
207                                                'show': status, 'message': message})
208                unread_messages_no = self.plugin.roster.nb_unread
209
210                if unread_messages_no > 0:
211                        text = i18n.ngettext(
212                                        'Gajim - one unread message',
213                                        'Gajim - %d unread messages',
214                                        unread_messages_no, None, unread_messages_no)
215                elif len(accounts) > 1:
216                        text = _('Gajim')
217                        self.current_row = 1
218                        self.table.resize(2,1)
219                        iconset = gajim.config.get('iconset')
220                        if not iconset:
221                                iconset = 'sun'
222                        file_path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
223                        for acct in accounts:
224                                message = acct['message']
225                                # before reducing the chars we should assure we send unicode, else
226                                # there are possible pango TBs on 'set_markup'
227                                if type(message) == str:
228                                        message = unicode(message, encoding='utf-8')
229                                message = gtkgui_helpers.reduce_chars_newlines(message, 50, 1)
230                                message = gtkgui_helpers.escape_for_pango_markup(message)
231                                self.add_status_row(file_path, acct['show'], '<span weight="bold">' + 
232                                        gtkgui_helpers.escape_for_pango_markup(acct['name']) + '</span>' 
233                                        + ' - ' + message)
234                                       
235                elif len(accounts) == 1:
236                        message = gtkgui_helpers.reduce_chars_newlines(accounts[0]['status_line'], 
237                                50, 1)
238                        message = gtkgui_helpers.escape_for_pango_markup(message)
239                        text = _('Gajim - %s') % message
240                else:
241                        text = _('Gajim - %s') % helpers.get_uf_show('offline')
242                self.text_lable.set_markup(text)
243                self.hbox.add(self.table)
244                self.win.add(self.hbox)
245
246class GCTooltip(BaseTooltip, StatusTable):
247        ''' Tooltip that is shown in the GC treeview '''
248        def __init__(self, plugin):
249                self.account = None
250                self.plugin = plugin
251                self.text_lable = gtk.Label()
252                self.text_lable.set_line_wrap(True)
253                self.text_lable.set_alignment(0., 0.)
254                self.text_lable.set_selectable(False)
255                BaseTooltip.__init__(self)
256               
257        def populate(self, contact):
258                if not contact:
259                        return
260                self.create_window()
261                hbox = gtk.HBox()
262                info = '<span size="large" weight="bold">' + contact.name + '</span>'
263                info += '\n<span weight="bold">' + _('Role: ') + '</span>' + \
264                         helpers.get_uf_role(contact.role)
265
266                info += '\n<span weight="bold">' + _('Affiliation: ') + '</span>' + \
267                        contact.affiliation.capitalize()
268
269                info += '\n<span weight="bold">' + _('Status: ') + \
270                                        '</span>' + helpers.get_uf_show(contact.show)
271               
272                if contact.status:
273                        status = contact.status.strip()
274                        if status != '':
275                                # escape markup entities
276                                info += ' - ' + gtkgui_helpers.escape_for_pango_markup(status)
277
278                self.text_lable.set_markup(info)
279                hbox.add(self.text_lable)
280                self.win.add(hbox)
281
282
283class RosterTooltip(BaseTooltip, StatusTable):
284        ''' Tooltip that is shown in the roster treeview '''
285        def __init__(self, plugin):
286                self.account = None
287                self.plugin = plugin
288               
289                self.image = gtk.Image()
290                self.image.set_alignment(0., 0.)
291                # padding is independent of the total length and better than alignment
292                self.image.set_padding(1, 2) 
293                BaseTooltip.__init__(self)
294                StatusTable.__init__(self)
295               
296        def populate(self, contacts):
297                if not contacts or len(contacts) == 0:
298                        return
299                self.create_window()
300                self.hbox = gtk.HBox()
301                self.hbox.set_homogeneous(False)
302                self.hbox.set_spacing(0)
303                self.create_table()
304                # primary contact
305                prim_contact = gajim.get_highest_prio_contact_from_contacts(contacts)
306               
307                # try to find the image for the contact status
308                state_file = prim_contact.show.replace(' ', '_')
309                transport = gajim.get_transport_name_from_jid(prim_contact.jid)
310                if transport:
311                        file_path = os.path.join(gajim.DATA_DIR, 'iconsets', 'transports', 
312                                transport , '16x16')
313                else:
314                        iconset = gajim.config.get('iconset')
315                        if not iconset:
316                                iconset = 'sun'
317                        file_path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
318
319                files = []
320                file_full_path = os.path.join(file_path, state_file)
321                files.append(file_full_path + '.png')
322                files.append(file_full_path + '.gif')
323                self.image.set_from_pixbuf(None)
324                for file in files:
325                        if os.path.exists(file):
326                                self.image.set_from_file(file)
327                                break
328               
329                info = '<span size="large" weight="bold">' + prim_contact.jid + '</span>'
330                info += '\n<span weight="bold">' + _('Name: ') + '</span>' + \
331                        gtkgui_helpers.escape_for_pango_markup(prim_contact.name)
332                info += '\n<span weight="bold">' + _('Subscription: ') + '</span>' + \
333                        gtkgui_helpers.escape_for_pango_markup(prim_contact.sub)
334
335                if prim_contact.keyID:
336                        keyID = None
337                        if len(prim_contact.keyID) == 8:
338                                keyID = prim_contact.keyID
339                        elif len(prim_contact.keyID) == 16:
340                                keyID = prim_contact.keyID[8:]
341                        if keyID:
342                                info += '\n<span weight="bold">' + _('OpenPGP: ') + \
343                                        '</span>' + gtkgui_helpers.escape_for_pango_markup(keyID)
344
345                single_line, resource_str, multiple_resource= '', '', False
346                num_resources = 0
347                for contact in contacts:
348                        if contact.resource:
349                                num_resources += 1
350                if num_resources > 1:
351                        self.current_row = 1
352                        self.table.resize(2,1)
353                        info += '\n<span weight="bold">' + _('Status: ') + '</span>'
354                        for contact in contacts:
355                                if contact.resource:
356                                        status_line = self.get_status_info(contact.resource, contact.priority, 
357                                                contact.show, contact.status)
358                                        self.add_status_row(file_path, contact.show, status_line)
359                                       
360                else: # only one resource
361                        if contact.resource:
362                                info += '\n<span weight="bold">' + _('Resource: ') + \
363                                        '</span>' + gtkgui_helpers.escape_for_pango_markup(
364                                                contact.resource) + ' (' + unicode(contact.priority) + ')'
365                        if contact.show:
366                                info += '\n<span weight="bold">' + _('Status: ') + \
367                                        '</span>' + helpers.get_uf_show(contact.show) 
368                                if contact.status:
369                                        status = contact.status.strip()
370                                        if status != '':
371                                                # reduce long status (no more than 130 chars on line and no more than 5 lines)
372                                                status = gtkgui_helpers.reduce_chars_newlines(status, 130, 5)
373                                                # escape markup entities.
374                                                info += ' - ' + gtkgui_helpers.escape_for_pango_markup(status)
375               
376                self.text_lable.set_markup(info)
377                self.hbox.pack_start(self.image, False, False)
378                self.hbox.pack_start(self.table, True, True)
379                self.win.add(self.hbox)
380
381class FileTransfersTooltip(BaseTooltip):
382        ''' Tooltip that is shown in the notification area '''
383        def __init__(self):
384                self.text_lable = gtk.Label()
385                self.text_lable.set_line_wrap(True)
386                self.text_lable.set_alignment(0, 0)
387                self.text_lable.set_selectable(False)
388                BaseTooltip.__init__(self)
389
390        def populate(self, file_props):
391                self.create_window()
392                self.hbox = gtk.HBox()
393                text = '<b>' + _('Name: ') + '</b>' 
394                name = file_props['name']
395                if file_props['type'] == 'r':
396                        (file_path, file_name) = os.path.split(file_props['file-name'])
397                else:
398                        file_name = file_props['name']
399                text += gtkgui_helpers.escape_for_pango_markup(file_name) 
400                text += '\n<b>' + _('Type: ') + '</b>'
401                if file_props['type'] == 'r':
402                        text += _('Download')
403                else:
404                        text += _('Upload')
405                if file_props['type'] == 'r':
406                        text += '\n<b>' + _('Sender: ') + '</b>'
407                        sender = unicode(file_props['sender']).split('/')[0]
408                        name = gajim.get_first_contact_instance_from_jid( 
409                                file_props['tt_account'], sender).name
410                else:
411                        text += '\n<b>' + _('Recipient: ') + '</b>' 
412                        receiver = file_props['receiver']
413                        if hasattr(receiver, 'name'):
414                                receiver = receiver.name
415                        receiver = receiver.split('/')[0]
416                        if receiver.find('@') == -1:
417                                name = receiver
418                        else:
419                                name = gajim.get_first_contact_instance_from_jid( 
420                                file_props['tt_account'], receiver).name
421                text +=  gtkgui_helpers.escape_for_pango_markup(name)
422                text += '\n<b>' + _('Size: ') + '</b>' 
423                text += helpers.convert_bytes(file_props['size'])
424                text += '\n<b>' + _('Transferred: ') + '</b>' 
425                transfered_len = 0
426                if file_props.has_key('received-len'):
427                        transfered_len = file_props['received-len']
428                text += helpers.convert_bytes(transfered_len)
429                text += '\n<b>' + _('Status: ') + '</b>' 
430                status = '' 
431                if not file_props.has_key('started') or not file_props['started']:
432                        status =  _('Not started')
433                elif file_props.has_key('connected'):
434                        if file_props.has_key('stopped') and \
435                                file_props['stopped'] == True:
436                                status = _('Stopped')
437                        elif file_props['completed']:
438                                        status = _('Completed')
439                        elif file_props['connected'] == False:
440                                if file_props['completed']:
441                                        status = _('Completed')
442                        else:
443                                if file_props.has_key('paused') and  \
444                                        file_props['paused'] == True:
445                                        status = _('Paused')
446                                elif file_props.has_key('stalled') and \
447                                        file_props['stalled'] == True:
448                                        #stalled is not paused. it is like 'frozen' it stopped alone
449                                        status = _('Stalled')
450                                else:
451                                        status = _('Transferring')
452                else:
453                        status =  _('Not started')
454               
455                text += status
456                self.text_lable.set_markup(text)
457                self.hbox.add(self.text_lable)
458                self.win.add(self.hbox)
Note: See TracBrowser for help on using the browser.