~ubuntu-branches/ubuntu/oneiric/emesene/oneiric-proposed

« back to all changes in this revision

Viewing changes to emesene/gui/gtkui/TrayIcon.py

  • Committer: Bazaar Package Importer
  • Author(s): Devid Antonio Filoni
  • Date: 2011-03-03 14:49:13 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20110303144913-0adl9cmw2s35lvzo
Tags: 2.0~git20110303-0ubuntu1
* New upstream git revision (LP: #728469).
* Remove debian/watch, debian/emesene.xpm, debian/install and
  debian/README.source files.
* Remove 21_svn2451_fix_avatar and 20_dont_build_own_libmimic patches.
* debian/control: modify python to python (>= 2.5) in Build-Depends field.
* debian/control: remove python-libmimic from Recommends field.
* debian/control: modify python-gtk2 (>= 2.10) to python-gtk2 (>= 2.12) in
  Depends field.
* debian/control: add python-appindicator and python-xmpp to Recommends
  field.
* debian/control: add python-papyon (>= 0.5.4) and python-webkit to Depends
  field.
* debian/control: update Description field.
* debian/control: add python-setuptools to Build-Depends field.
* debian/control: move python-dbus and python-notify to Depends field.
* Update debian/copyright file.
* Update debian/links file.
* debian/menu: update description field.
* Bump Standards-Version to 3.9.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
#    This file is part of emesene.
 
4
#
 
5
#    emesene is free software; you can redistribute it and/or modify
 
6
#    it under the terms of the GNU General Public License as published by
 
7
#    the Free Software Foundation; either version 3 of the License, or
 
8
#    (at your option) any later version.
 
9
#
 
10
#    emesene is distributed in the hope that it will be useful,
 
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
#    GNU General Public License for more details.
 
14
#
 
15
#    You should have received a copy of the GNU General Public License
 
16
#    along with emesene; if not, write to the Free Software
 
17
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
 
 
19
import os
 
20
import gtk
 
21
import time
 
22
import gobject
 
23
 
 
24
import extension
 
25
from e3 import status
 
26
 
 
27
import gui
 
28
import utils
 
29
 
 
30
import Renderers
 
31
 
 
32
from BaseTray import BaseTray
 
33
 
 
34
class TrayIcon(gtk.StatusIcon, BaseTray):
 
35
    """
 
36
    A widget that implements the tray icon of emesene for gtk
 
37
    """
 
38
    NAME = 'Tray Icon'
 
39
    DESCRIPTION = 'The gtk tray icon'
 
40
    AUTHOR = 'Mariano Guerra'
 
41
    WEBSITE = 'www.emesene.org'
 
42
 
 
43
    def __init__(self, handler, main_window=None):
 
44
        """
 
45
        constructor
 
46
 
 
47
        handler -- a e3common.Handler.TrayIconHandler object
 
48
        """
 
49
        BaseTray.__init__(self)
 
50
 
 
51
        gtk.StatusIcon.__init__(self)
 
52
        self.handler = handler
 
53
 
 
54
        self.main_window = main_window
 
55
        self.last_new_message = None
 
56
 
 
57
        self.connect('activate', self._on_activate)
 
58
        self.connect('popup-menu', self._on_popup)
 
59
 
 
60
        self.set_login()
 
61
        self.set_visible(True)
 
62
 
 
63
        self.set_tooltip("emesene")
 
64
 
 
65
    def set_login(self):
 
66
        """
 
67
        method called to set the state to the login window
 
68
        """
 
69
        self.menu = LoginMenu(self.handler, self.main_window)
 
70
        self.menu.show_all()
 
71
        self.set_from_file(self.handler.theme.logo)
 
72
 
 
73
    def set_main(self, session):
 
74
        """
 
75
        method called to set the state to the main window
 
76
        """
 
77
        self.handler.session = session
 
78
        self.handler.session.signals.status_change_succeed.subscribe(
 
79
                                                      self._on_change_status)
 
80
        self.handler.session.signals.conv_message.subscribe(self._on_message)
 
81
        self.handler.session.signals.message_read.subscribe(self._on_read)
 
82
        self.menu = MainMenu(self.handler, self.main_window)
 
83
        self.menu.show_all()
 
84
        self.set_tooltip("emesene - " + self.handler.session.account.account)
 
85
 
 
86
    def _on_message(self, cid, account, msgobj, cedict={}):
 
87
        """
 
88
        Blink tray icon and save newest unread message
 
89
        """
 
90
 
 
91
        conv_manager = self._get_conversation_manager(cid, account)
 
92
 
 
93
        if conv_manager and not conv_manager.is_active():
 
94
            self.set_blinking(True)
 
95
            self.last_new_message = cid
 
96
 
 
97
    def _on_read(self, conv):
 
98
        """
 
99
        Stop tray blinking and resets the newest unread message reference
 
100
        """
 
101
        self.set_blinking(False)
 
102
        self.last_new_message = None
 
103
 
 
104
    def _on_activate(self, trayicon):
 
105
        """
 
106
        callback called when the status icon is activated
 
107
        (includes clicking the icon)
 
108
        """
 
109
 
 
110
        if self.last_new_message is not None and self.get_blinking():
 
111
            # show the tab with the latest message
 
112
            cid = self.last_new_message
 
113
            conv_manager = self._get_conversation_manager(cid)
 
114
 
 
115
            if conv_manager:
 
116
                conversation = conv_manager.conversations[cid]
 
117
                conv_manager.present(conversation)
 
118
        else:
 
119
            self.handler.on_hide_show_mainwindow(self.main_window)
 
120
 
 
121
    def _on_change_status(self,stat):
 
122
        """
 
123
        change the icon in the tray according to user's state
 
124
        """
 
125
        if stat not in status.ALL or stat == -1:
 
126
            return
 
127
        self.set_from_file(self.handler.theme.status_icons_panel[stat])
 
128
 
 
129
    def _on_popup(self, trayicon, button, activate_time):
 
130
        """
 
131
        callback called when the popup of the status icon is activated
 
132
        (usually through right-clicking the status icon)
 
133
        """
 
134
        position = None
 
135
        if os.name == 'posix':
 
136
            position = gtk.status_icon_position_menu
 
137
        self.menu.popup(None, None, position, button, activate_time, trayicon)
 
138
 
 
139
class LoginMenu(gtk.Menu):
 
140
    """
 
141
    a widget that represents the menu displayed on the trayicon on the
 
142
    login window
 
143
    """
 
144
 
 
145
    def __init__(self, handler, main_window=None):
 
146
        """
 
147
        constructor
 
148
 
 
149
        handler -- a e3common.Handler.TrayIconHandler object
 
150
        """
 
151
        gtk.Menu.__init__(self)
 
152
        self.handler = handler
 
153
        self.hide_show_mainwindow = gtk.MenuItem(_('Hide/Show emesene'))
 
154
        self.hide_show_mainwindow.connect('activate',
 
155
            lambda *args: self.handler.on_hide_show_mainwindow(main_window))
 
156
        self.quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
 
157
        self.quit.connect('activate',
 
158
            lambda *args: self.handler.on_quit_selected())
 
159
            
 
160
        self.append(self.hide_show_mainwindow)
 
161
        self.append(self.quit)
 
162
 
 
163
class MainMenu(gtk.Menu):
 
164
    """
 
165
    a widget that represents the menu displayed on the trayicon on the
 
166
    main window
 
167
    """
 
168
 
 
169
    def __init__(self, handler, main_window=None):
 
170
        """
 
171
        constructor
 
172
 
 
173
        handler -- a e3common.Handler.TrayIconHandler object
 
174
        """
 
175
        gtk.Menu.__init__(self)
 
176
        self.handler = handler
 
177
 
 
178
        StatusMenu = extension.get_default('menu status')
 
179
        self.status = gtk.ImageMenuItem(_('Status'))
 
180
        self.status.set_image(gtk.image_new_from_stock(gtk.STOCK_CONVERT,
 
181
            gtk.ICON_SIZE_MENU))
 
182
        self.status_menu = StatusMenu(handler.on_status_selected)
 
183
        self.status.set_submenu(self.status_menu)
 
184
 
 
185
        self.list = gtk.MenuItem(_('Contacts'))
 
186
        self.list_contacts = ContactsMenu(handler, main_window)
 
187
        self.list.set_submenu(self.list_contacts)
 
188
 
 
189
        self.hide_show_mainwindow = gtk.MenuItem(_('Hide/Show emesene'))
 
190
        self.hide_show_mainwindow.connect('activate',
 
191
            lambda *args: self.handler.on_hide_show_mainwindow(main_window))
 
192
 
 
193
        self.disconnect = gtk.ImageMenuItem(gtk.STOCK_DISCONNECT)
 
194
        self.disconnect.connect('activate',
 
195
            lambda *args: self.handler.on_disconnect_selected())
 
196
        self.quit = gtk.ImageMenuItem(gtk.STOCK_QUIT)
 
197
        self.quit.connect('activate',
 
198
            lambda *args: self.handler.on_quit_selected())
 
199
 
 
200
        self.append(self.hide_show_mainwindow)
 
201
        self.append(self.status)
 
202
        self.append(self.list)        
 
203
        self.append(self.disconnect)
 
204
        self.append(gtk.SeparatorMenuItem())
 
205
        self.append(self.quit)
 
206
 
 
207
class ContactsMenu(gtk.Menu):
 
208
    """
 
209
    a gtk menu that contains session's contacts
 
210
    """
 
211
    NAME = 'Contacts Menu'
 
212
    DESCRIPTION = _('A menu with sessions\' contacts')
 
213
    AUTHOR = 'Riccardo (C10uD)'
 
214
    WEBSITE = 'www.emesene.org'
 
215
 
 
216
    def __init__(self, handler, main_window=None):
 
217
        """
 
218
        constructor
 
219
        """
 
220
        gtk.Menu.__init__(self)
 
221
        self.handler = handler
 
222
        self.main_window = main_window
 
223
        self.item_to_contacts = {}
 
224
        self.contacts_to_item = {}
 
225
        self.avatar_size = 32
 
226
 
 
227
        self.contactmanager = self.handler.session.contacts
 
228
        
 
229
        for contact in self.contactmanager.get_online_list():
 
230
            self.__append_contact(contact)
 
231
 
 
232
        self.handler.session.signals.contact_attr_changed.subscribe(self._on_contact_change_something)
 
233
 
 
234
        # TODO: find out why gtk ImageMenuItem does not work as expected
 
235
 
 
236
    def __append_contact(self, contact):
 
237
        """
 
238
        appends a contact to our submenu
 
239
        """
 
240
        #item = gtk.ImageMenuItem()
 
241
        item = gtk.MenuItem()
 
242
        item.set_label(Renderers.msnplus_to_plain_text(contact.nick))
 
243
        #pict = self.__get_contact_pixbuf_or_default(contact)
 
244
        #item.set_image(pict)
 
245
        item.connect('activate', self._on_contact_clicked)    
 
246
        self.item_to_contacts[item] = contact
 
247
        self.contacts_to_item[contact.account] = item
 
248
 
 
249
        item.show()
 
250
        self.add(item)
 
251
                
 
252
    def _on_contact_change_something(self, *args):
 
253
        """
 
254
        update the menu when contacts change something
 
255
        """
 
256
        if len(args) == 3:
 
257
            account, type_change, value_change = args
 
258
        elif len(args) == 4:
 
259
            account, type_change, value_change, do_notify = args
 
260
        
 
261
        if type_change == 'status':
 
262
            if value_change > 0:
 
263
                if account in self.contacts_to_item:
 
264
                    return
 
265
                self.__append_contact(self.contactmanager.get(account))
 
266
            else: # offline
 
267
                if account in self.contacts_to_item:
 
268
                    self.remove(self.contacts_to_item[account])
 
269
                    del self.item_to_contacts[self.contacts_to_item[account]]
 
270
                    del self.contacts_to_item[account]
 
271
 
 
272
        if type_change == 'nick':
 
273
            if account in self.contacts_to_item:
 
274
                nick = self.item_to_contacts[self.contacts_to_item[account]].nick
 
275
                self.contacts_to_item[account].set_label(nick)
 
276
 
 
277
        if type_change == 'picture':
 
278
        #TODO: fixme
 
279
            return
 
280
            if account in self.contacts_to_item:
 
281
                contact = self.item_to_contacts[self.contacts_to_item[account]]
 
282
                pict = self.__get_contact_pixbuf_or_default(contact)
 
283
                self.contacts_to_item[account].set_image(pict)
 
284
 
 
285
    def _on_contact_clicked(self, menu_item):
 
286
        """
 
287
        called when contacts are clicked
 
288
        """
 
289
        acc = self.item_to_contacts[menu_item].account
 
290
        cid = time.time()
 
291
        self.main_window.content.on_new_conversation(cid, [acc], other_started=False)
 
292
        self.handler.session.new_conversation(acc, cid)
 
293
 
 
294
    def __get_contact_pixbuf_or_default(self, contact):
 
295
        '''try to return a pixbuf of the user picture or the default
 
296
        picture
 
297
        '''
 
298
        if contact.picture:
 
299
            try:
 
300
                animation = gtk.gdk.PixbufAnimation(contact.picture)
 
301
            except gobject.GError:
 
302
                pix = utils.safe_gtk_pixbuf_load(gui.theme.user,
 
303
                        (self.avatar_size, self.avatar_size))
 
304
                picture = gtk.image_new_from_pixbuf(pix)
 
305
                return picture
 
306
 
 
307
            if animation.is_static_image():
 
308
                pix = utils.safe_gtk_pixbuf_load(contact.picture,
 
309
                        (self.avatar_size, self.avatar_size))
 
310
                picture = gtk.image_new_from_pixbuf(pix)
 
311
            else:
 
312
                picture = gtk.image_new_from_animation(animation)
 
313
 
 
314
        else:
 
315
            pix = utils.safe_gtk_pixbuf_load(gui.theme.user,
 
316
                        (self.avatar_size, self.avatar_size))
 
317
            picture = gtk.image_new_from_pixbuf(pix)
 
318
 
 
319
        return picture
 
320