root/branches/gajim_0.8/src/systraywin32.py

Revision 3104, 9.5 kB (checked in by nk, 3 years ago)

try to catch an exception

Line 
1## src/systraywin32.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## code initially based on
10## http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/334779
11##
12##      Copyright (C) 2003-2005 Gajim Team
13##
14## This program is free software; you can redistribute it and/or modify
15## it under the terms of the GNU General Public License as published
16## by the Free Software Foundation; version 2 only.
17##
18## This program is distributed in the hope that it will be useful,
19## but WITHOUT ANY WARRANTY; without even the implied warranty of
20## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21## GNU General Public License for more details.
22##
23
24
25import win32gui
26import win32con # winapi contants
27import systray
28import gtk
29import os
30
31WM_TASKBARCREATED = win32gui.RegisterWindowMessage('TaskbarCreated')
32WM_TRAYMESSAGE = win32con.WM_USER + 20
33
34from common import gajim
35from common import i18n
36_ = i18n._
37APP = i18n.APP
38gtk.glade.bindtextdomain(APP, i18n.DIR)
39gtk.glade.textdomain(APP)
40
41GTKGUI_GLADE = 'gtkgui.glade'
42
43class SystrayWINAPI:
44        def __init__(self, gtk_window):
45                self._window = gtk_window
46                self._hwnd = gtk_window.window.handle
47                self._message_map = {}
48
49                self.notify_icon = None           
50
51                # Sublass the window and inject a WNDPROC to process messages.
52                self._oldwndproc = win32gui.SetWindowLong(self._hwnd, win32con.GWL_WNDPROC,
53                                                                                                self._wndproc)
54
55                gtk_window.connect('unrealize', self.remove)
56
57
58        def add_notify_icon(self, menu, hicon=None, tooltip=None):
59                """ Creates a notify icon for the gtk window. """
60                if not self.notify_icon:
61                        if not hicon:
62                                hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
63                        self.notify_icon = NotifyIcon(self._hwnd, hicon, tooltip)
64
65                        # Makes redraw if the taskbar is restarted.   
66                        self.message_map({WM_TASKBARCREATED: self.notify_icon._redraw})
67
68
69        def message_map(self, msg_map={}):
70                """ Maps message processing to callback functions ala win32gui. """
71                if msg_map:
72                        if self._message_map:
73                                duplicatekeys = [key for key in msg_map.keys()
74                                                                if self._message_map.has_key(key)]
75                               
76                                for key in duplicatekeys:
77                                        new_value = msg_map[key]
78                                       
79                                        if isinstance(new_value, list):
80                                                raise TypeError('Dict cannot have list values')
81                                       
82                                        value = self._message_map[key]
83                                       
84                                        if new_value != value:
85                                                new_value = [new_value]
86                                               
87                                                if isinstance(value, list):
88                                                        value += new_value
89                                                else:
90                                                        value = [value] + new_value
91                                               
92                                                msg_map[key] = value
93                        self._message_map.update(msg_map)
94
95        def message_unmap(self, msg, callback=None):
96                if self._message_map.has_key(msg):
97                        if callback:
98                                cblist = self._message_map[key]
99                                if isinstance(cblist, list):
100                                        if not len(cblist) < 2:
101                                                for i in range(len(cblist)):
102                                                        if cblist[i] == callback:
103                                                                del self._message_map[key][i]
104                                                                return
105                        del self._message_map[key]
106
107        def remove_notify_icon(self):
108                """ Removes the notify icon. """
109                if self.notify_icon:
110                        self.notify_icon.remove()
111                        self.notify_icon = None
112
113        def remove(self, *args):
114                """ Unloads the extensions. """
115                self._message_map = {}
116                self.remove_notify_icon()
117                self = None
118
119        def show_balloon_tooltip(self, title, text, timeout=10,
120                                                        icon=win32gui.NIIF_NONE):
121                """ Shows a baloon tooltip. """
122                if not self.notify_icon:
123                        self.add_notifyicon()
124                self.notify_icon.show_balloon(title, text, timeout, icon)
125
126        def _wndproc(self, hwnd, msg, wparam, lparam):
127                """ A WINDPROC to process window messages. """
128                if self._message_map.has_key(msg):
129                        callback = self._message_map[msg]
130                        if isinstance(callback, list):
131                                for cb in callback:
132                                        cb(hwnd, msg, wparam, lparam)
133                        else:
134                                callback(hwnd, msg, wparam, lparam)
135
136                return win32gui.CallWindowProc(self._oldwndproc, hwnd, msg, wparam,
137                                                                        lparam)
138                                                                       
139
140class NotifyIcon:
141
142        def __init__(self, hwnd, hicon, tooltip=None):
143                self._hwnd = hwnd
144                self._id = 0
145                self._flags = win32gui.NIF_MESSAGE | win32gui.NIF_ICON
146                self._callbackmessage = WM_TRAYMESSAGE
147                self._hicon = hicon
148
149                win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self._get_nid())
150                if tooltip: self.set_tooltip(tooltip)
151
152
153        def _get_nid(self):
154                """ Function to initialise & retrieve the NOTIFYICONDATA Structure. """
155                nid = (self._hwnd, self._id, self._flags, self._callbackmessage,
156                        self._hicon)
157                nid = list(nid)
158
159                if not hasattr(self, '_tip'): self._tip = ''
160                nid.append(self._tip)
161
162                if not hasattr(self, '_info'): self._info = ''
163                nid.append(self._info)
164                       
165                if not hasattr(self, '_timeout'): self._timeout = 0
166                nid.append(self._timeout)
167
168                if not hasattr(self, '_infotitle'): self._infotitle = ''
169                nid.append(self._infotitle)
170                       
171                if not hasattr(self, '_infoflags'):self._infoflags = win32gui.NIIF_NONE
172                nid.append(self._infoflags)
173
174                return tuple(nid)
175               
176       
177        def remove(self):
178                """ Removes the tray icon. """
179                try:
180                        win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self._get_nid())
181                except: # maybe except just pywintypes.error ? anyways..
182                        pass
183
184
185        def set_tooltip(self, tooltip):
186                """ Sets the tray icon tooltip. """
187                self._flags = self._flags | win32gui.NIF_TIP
188                self._tip = tooltip
189                win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self._get_nid())
190               
191               
192        def show_balloon(self, title, text, timeout=10, icon=win32gui.NIIF_NONE):
193                """ Shows a balloon tooltip from the tray icon. """
194                self._flags = self._flags | win32gui.NIF_INFO
195                self._infotitle = title
196                self._info = text
197                self._timeout = timeout * 1000
198                self._infoflags = icon
199                win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self._get_nid())
200
201        def _redraw(self, *args):
202                """ Redraws the tray icon. """
203                self.remove
204                win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self._get_nid())
205
206
207class SystrayWin32(systray.Systray):
208        def __init__(self, plugin):
209                # Note: gtk window must be realized before installing extensions.
210                systray.Systray.__init__(self, plugin)
211                self.plugin = plugin
212                self.jids = []
213                self.status = 'offline'
214                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'systray_context_menu', APP)
215                self.systray_context_menu = self.xml.get_widget('systray_context_menu')
216               
217                self.tray_ico_imgs = self.load_icos()
218               
219                self.systray_winapi = SystrayWINAPI(self.plugin.roster.window)
220
221                self.xml.signal_autoconnect(self)
222               
223                # Set up the callback messages
224                self.systray_winapi.message_map({
225                        WM_TRAYMESSAGE: self.on_clicked
226                        }) 
227
228        def show_icon(self):
229                #self.systray_winapi.add_notify_icon(self.systray_context_menu, tooltip = 'Gajim')
230                #self.systray_winapi.notify_icon.menu = self.systray_context_menu
231                # do not remove set_img does both above. maybe I can only change img without readding
232                # the notify icon? F$ck WINAPI
233                self.set_img()
234
235        def hide_icon(self):
236                self.systray_winapi.remove_notify_icon()
237
238        def on_clicked(self, hwnd, message, wparam, lparam):
239                if lparam == win32con.WM_RBUTTONUP: # Right click
240                        self.make_menu()
241                        self.systray_winapi.notify_icon.menu.popup(None, None, None, 0, 0)
242                elif lparam == win32con.WM_MBUTTONUP: # Middle click
243                        self.on_middle_click()
244                elif lparam == win32con.WM_LBUTTONUP: # Left click
245                        self.on_left_click()
246
247        def add_jid(self, jid, account):
248                l = [account, jid]
249                if not l in self.jids:
250                        self.jids.append(l)
251                        self.set_img()
252                # we append to the number of unread messages
253                nb = self.plugin.roster.nb_unread
254                for acct in gajim.connections:
255                        # in chat / groupchat windows
256                        for kind in ['chats', 'gc']:
257                                jids = self.plugin.windows[acct][kind]
258                                for jid in jids:
259                                        if jid != 'tabbed':
260                                                nb += jids[jid].nb_unread[jid]
261               
262                #FIXME: prepare me for transltaion (ngeetext() and all) for 0.9
263                if nb > 1:
264                        text = 'Gajim - %s unread messages' % nb
265                else:
266                        text = 'Gajim - 1 unread message'
267                self.systray_winapi.notify_icon.set_tooltip(text)
268
269        def remove_jid(self, jid, account):
270                l = [account, jid]
271                if l in self.jids:
272                        self.jids.remove(l)
273                        self.set_img()
274                # we remove from the number of unread messages
275                nb = self.plugin.roster.nb_unread
276                for acct in gajim.connections:
277                        # in chat / groupchat windows
278                        for kind in ['chats', 'gc']:
279                                for jid in self.plugin.windows[acct][kind]:
280                                        if jid != 'tabbed':
281                                                nb += self.plugin.windows[acct][kind][jid].nb_unread[jid]
282               
283                #FIXME: prepare me for transltaion (ngeetext() and all) for 0.9
284                if nb > 1:
285                        text = 'Gajim - %s unread messages' % nb
286                elif nb == 1:
287                        text = 'Gajim - 1 unread message'
288                else:
289                        text = 'Gajim'
290                self.systray_winapi.notify_icon.set_tooltip(text)
291               
292
293        def set_img(self):
294                self.systray_winapi.remove_notify_icon()
295                if len(self.jids) > 0:
296                        state = 'message'
297                else:
298                        state = self.status # FIXME: get LoadImage code to images[] dict in systray.py
299                hicon = self.tray_ico_imgs[state]
300               
301                self.systray_winapi.add_notify_icon(self.systray_context_menu, hicon, 'Gajim')
302                self.systray_winapi.notify_icon.menu = self.systray_context_menu
303
304        def load_icos(self):
305                '''load .ico files and return them to a dic of SHOW --> img_obj'''
306                #iconset = gajim.config.get('iconset')
307                #if not iconset:
308                #       iconset = 'sun'
309               
310                iconset = 'gnome' # FIXME: add icos in all folders (icos are not as good as pngs in colors etc..)
311               
312                imgs = {}
313                path = os.path.join(gajim.DATA_DIR, 'iconsets/' + iconset + '/16x16/icos/')
314                states_list = gajim.SHOW_LIST
315                states_list.append('message') # trayicon apart from show holds message state too
316                for state in states_list:
317                        path_to_ico = path + state + '.ico'
318                        if os.path.exists(path_to_ico):
319                                hinst = win32gui.GetModuleHandle(None)
320                                img_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
321                                image = win32gui.LoadImage(hinst, path_to_ico, win32con.IMAGE_ICON, 
322                                        0, 0, img_flags)
323                                imgs[state] = image
324               
325                return imgs
Note: See TracBrowser for help on using the browser.