Merge lp:~jconti/gm-notify/messaging-menu into lp:gm-notify

Proposed by Dimitri John Ledkov
Status: Merged
Approved by: Mateusz Balbus
Approved revision: 96
Merged at revision: 80
Proposed branch: lp:~jconti/gm-notify/messaging-menu
Merge into: lp:gm-notify
Diff against target: 1304 lines (+762/-216)
10 files modified
MANIFEST (+2/-0)
data/gm-notify.convert (+6/-0)
data/gm-notify.desktop (+2/-2)
data/net.launchpad.gm-notify.gschema.xml (+25/-0)
gm-config.ui (+439/-0)
gm-notify (+150/-106)
gm-notify-config (+84/-75)
gm_notify_keyring.py (+40/-24)
gtalk.py (+11/-5)
setup.py (+3/-4)
To merge this branch: bzr merge lp:~jconti/gm-notify/messaging-menu
Reviewer Review Type Date Requested Status
Mateusz Balbus Approve
Review via email: mp+155163@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Dimitri John Ledkov (xnox) wrote :

This looks very good, I'm considering to upload this into Ubuntu Archive, as it has MessagingMenu support, among many other improvements.

Revision history for this message
Mateusz Balbus (mate-ob) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'MANIFEST'
--- MANIFEST 2010-09-18 13:06:25 +0000
+++ MANIFEST 2013-03-25 04:23:21 +0000
@@ -4,12 +4,14 @@
4keyring.py4keyring.py
5setup.py5setup.py
6gm-config.glade6gm-config.glade
7gm-config.ui
7gtalk.py8gtalk.py
8data/checking.gif9data/checking.gif
9data/gm-notify10data/gm-notify
10data/gm-notify-config.desktop11data/gm-notify-config.desktop
11data/gm-notify.desktop12data/gm-notify.desktop
12data/gm-notify.schemas13data/gm-notify.schemas
14data/net.launchpad.gm-notify.gschema.xml
13po/gm-notify.pot15po/gm-notify.pot
14po/bg/gm-notify.mo16po/bg/gm-notify.mo
15po/bg/gm-notify.po17po/bg/gm-notify.po
1618
=== added file 'data/gm-notify.convert'
--- data/gm-notify.convert 1970-01-01 00:00:00 +0000
+++ data/gm-notify.convert 2013-03-25 04:23:21 +0000
@@ -0,0 +1,6 @@
1[net.launchpad.gm-notify]
2play-sound = /apps/gm-notify/play_sound
3ignore-inbox = /apps/gm-notify/ignore_inbox
4soundfile = /apps/gm-notify/soundfile
5mailboxes = /apps/gm-notify/mailboxes
6openclient = /apps/gm-notify/openclient
07
=== modified file 'data/gm-notify.desktop'
--- data/gm-notify.desktop 2010-06-22 19:46:39 +0000
+++ data/gm-notify.desktop 2013-03-25 04:23:21 +0000
@@ -1,8 +1,8 @@
1[Desktop Entry]1[Desktop Entry]
2Encoding=UTF-82Encoding=UTF-8
3Name=Google Mail3Name=GMail Notifier
4Comment=An E-Mail Notifier for Google Mail4Comment=An E-Mail Notifier for Google Mail
5Icon=applications-email-panel5Icon=evolution
6Type=Application6Type=Application
7Exec=gm-notify7Exec=gm-notify
8StartupNotify=False8StartupNotify=False
99
=== added file 'data/net.launchpad.gm-notify.gschema.xml'
--- data/net.launchpad.gm-notify.gschema.xml 1970-01-01 00:00:00 +0000
+++ data/net.launchpad.gm-notify.gschema.xml 2013-03-25 04:23:21 +0000
@@ -0,0 +1,25 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<schemalist>
3 <schema path="/net/launchpad/gm-notify/" id="net.launchpad.gm-notify" gettext-domain="gm-notify">
4 <key type="b" name="play-sound">
5 <default>false</default>
6 <summary>Shall we play a sound on new mail?</summary>
7 </key>
8 <key type="b" name="ignore-inbox">
9 <default>false</default>
10 <summary>If true, there won't be notifications regarding just your inbox.</summary>
11 </key>
12 <key type="s" name="soundfile">
13 <default>''</default>
14 <summary>Path to the soundfile which should be played when a new mail arrives</summary>
15 </key>
16 <key type="as" name="mailboxes">
17 <default>[]</default>
18 <summary>List containing mailboxes to check</summary>
19 </key>
20 <key type="b" name="openclient">
21 <default>false</default>
22 <summary>Shall we open the default mail client or webinterface?</summary>
23 </key>
24 </schema>
25</schemalist>
026
=== added file 'gm-config.ui'
--- gm-config.ui 1970-01-01 00:00:00 +0000
+++ gm-config.ui 2013-03-25 04:23:21 +0000
@@ -0,0 +1,439 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<interface>
3 <!-- interface-requires gtk+ 3.0 -->
4 <object class="GtkWindow" id="gmnotify_config_main">
5 <property name="can_focus">False</property>
6 <property name="border_width">10</property>
7 <property name="title" translatable="yes">GMail Notifier</property>
8 <property name="window_position">center</property>
9 <property name="icon_name">evolution</property>
10 <child>
11 <object class="GtkBox" id="vbox_main">
12 <property name="visible">True</property>
13 <property name="can_focus">False</property>
14 <property name="orientation">vertical</property>
15 <property name="spacing">10</property>
16 <child>
17 <object class="GtkNotebook" id="notebook_main">
18 <property name="visible">True</property>
19 <property name="can_focus">True</property>
20 <child>
21 <object class="GtkBox" id="vbox1">
22 <property name="can_focus">False</property>
23 <property name="orientation">vertical</property>
24 <child>
25 <object class="GtkGrid" id="grid1">
26 <property name="visible">True</property>
27 <property name="can_focus">False</property>
28 <property name="row_homogeneous">True</property>
29 <property name="column_homogeneous">True</property>
30 <child>
31 <object class="GtkLabel" id="label_user">
32 <property name="visible">True</property>
33 <property name="can_focus">False</property>
34 <property name="label" translatable="yes">E-Mail:</property>
35 </object>
36 <packing>
37 <property name="left_attach">0</property>
38 <property name="top_attach">0</property>
39 <property name="width">1</property>
40 <property name="height">1</property>
41 </packing>
42 </child>
43 <child>
44 <object class="GtkLabel" id="label_password">
45 <property name="visible">True</property>
46 <property name="can_focus">False</property>
47 <property name="label" translatable="yes">Password:</property>
48 </object>
49 <packing>
50 <property name="left_attach">0</property>
51 <property name="top_attach">1</property>
52 <property name="width">1</property>
53 <property name="height">1</property>
54 </packing>
55 </child>
56 <child>
57 <object class="GtkEntry" id="input_user">
58 <property name="visible">True</property>
59 <property name="can_focus">True</property>
60 <property name="invisible_char">●</property>
61 <property name="invisible_char_set">True</property>
62 </object>
63 <packing>
64 <property name="left_attach">1</property>
65 <property name="top_attach">0</property>
66 <property name="width">1</property>
67 <property name="height">1</property>
68 </packing>
69 </child>
70 <child>
71 <object class="GtkEntry" id="input_password">
72 <property name="visible">True</property>
73 <property name="can_focus">True</property>
74 <property name="visibility">False</property>
75 <property name="invisible_char">●</property>
76 <property name="invisible_char_set">True</property>
77 <signal name="focus-out-event" handler="on_input_password_focus_out_event" swapped="no"/>
78 </object>
79 <packing>
80 <property name="left_attach">1</property>
81 <property name="top_attach">1</property>
82 <property name="width">1</property>
83 <property name="height">1</property>
84 </packing>
85 </child>
86 <child>
87 <object class="GtkImage" id="image_credentials">
88 <property name="visible">True</property>
89 <property name="can_focus">False</property>
90 <property name="stock">gtk-stop</property>
91 </object>
92 <packing>
93 <property name="left_attach">0</property>
94 <property name="top_attach">2</property>
95 <property name="width">1</property>
96 <property name="height">1</property>
97 </packing>
98 </child>
99 <child>
100 <object class="GtkLabel" id="label_credentials">
101 <property name="visible">True</property>
102 <property name="can_focus">False</property>
103 <property name="label" translatable="yes">Please enter credentials</property>
104 </object>
105 <packing>
106 <property name="left_attach">1</property>
107 <property name="top_attach">2</property>
108 <property name="width">1</property>
109 <property name="height">1</property>
110 </packing>
111 </child>
112 </object>
113 <packing>
114 <property name="expand">True</property>
115 <property name="fill">True</property>
116 <property name="position">0</property>
117 </packing>
118 </child>
119 <child>
120 <object class="GtkHSeparator" id="seperator_account">
121 <property name="visible">True</property>
122 <property name="can_focus">False</property>
123 </object>
124 <packing>
125 <property name="expand">False</property>
126 <property name="fill">True</property>
127 <property name="position">2</property>
128 </packing>
129 </child>
130 <child>
131 <object class="GtkBox" id="vbox2">
132 <property name="visible">True</property>
133 <property name="can_focus">False</property>
134 <property name="border_width">10</property>
135 <property name="orientation">vertical</property>
136 <property name="spacing">5</property>
137 <child>
138 <object class="GtkLabel" id="label_click">
139 <property name="visible">True</property>
140 <property name="can_focus">False</property>
141 <property name="xalign">0</property>
142 <property name="label" translatable="yes">Click on applet opens:</property>
143 </object>
144 <packing>
145 <property name="expand">False</property>
146 <property name="fill">True</property>
147 <property name="position">0</property>
148 </packing>
149 </child>
150 <child>
151 <object class="GtkAlignment" id="alignment1">
152 <property name="visible">True</property>
153 <property name="can_focus">False</property>
154 <property name="left_padding">20</property>
155 <child>
156 <object class="GtkRadioButton" id="radiobutton_openweb">
157 <property name="label" translatable="yes">GMail webinterface</property>
158 <property name="visible">True</property>
159 <property name="can_focus">True</property>
160 <property name="receives_default">False</property>
161 <property name="relief">none</property>
162 <property name="xalign">0.5</property>
163 <property name="active">True</property>
164 <property name="draw_indicator">True</property>
165 </object>
166 </child>
167 </object>
168 <packing>
169 <property name="expand">False</property>
170 <property name="fill">True</property>
171 <property name="position">1</property>
172 </packing>
173 </child>
174 <child>
175 <object class="GtkAlignment" id="alignment2">
176 <property name="visible">True</property>
177 <property name="can_focus">False</property>
178 <property name="left_padding">20</property>
179 <child>
180 <object class="GtkRadioButton" id="radiobutton_openclient">
181 <property name="label" translatable="yes">Current default mail client</property>
182 <property name="visible">True</property>
183 <property name="can_focus">True</property>
184 <property name="receives_default">False</property>
185 <property name="xalign">0.5</property>
186 <property name="yalign">0.50999999046325684</property>
187 <property name="active">True</property>
188 <property name="draw_indicator">True</property>
189 <property name="group">radiobutton_openweb</property>
190 </object>
191 </child>
192 </object>
193 <packing>
194 <property name="expand">False</property>
195 <property name="fill">True</property>
196 <property name="position">2</property>
197 </packing>
198 </child>
199 </object>
200 <packing>
201 <property name="expand">False</property>
202 <property name="fill">True</property>
203 <property name="position">3</property>
204 </packing>
205 </child>
206 </object>
207 </child>
208 <child type="tab">
209 <object class="GtkLabel" id="label_account">
210 <property name="visible">True</property>
211 <property name="can_focus">False</property>
212 <property name="label" translatable="yes">Account</property>
213 </object>
214 <packing>
215 <property name="tab_fill">False</property>
216 </packing>
217 </child>
218 <child>
219 <object class="GtkBox" id="vbox_enhanced">
220 <property name="visible">True</property>
221 <property name="can_focus">False</property>
222 <property name="border_width">10</property>
223 <property name="orientation">vertical</property>
224 <property name="spacing">5</property>
225 <child>
226 <object class="GtkCheckButton" id="checkbutton_sound">
227 <property name="label" translatable="yes">Play sound when new message arrives</property>
228 <property name="visible">True</property>
229 <property name="can_focus">True</property>
230 <property name="receives_default">False</property>
231 <property name="relief">half</property>
232 <property name="xalign">0.5</property>
233 <property name="draw_indicator">True</property>
234 <signal name="toggled" handler="on_checkbutton_sound_toggled" swapped="no"/>
235 </object>
236 <packing>
237 <property name="expand">False</property>
238 <property name="fill">True</property>
239 <property name="position">0</property>
240 </packing>
241 </child>
242 <child>
243 <object class="GtkFileChooserButton" id="fcbutton_sound">
244 <property name="visible">True</property>
245 <property name="can_focus">False</property>
246 <property name="create_folders">False</property>
247 </object>
248 <packing>
249 <property name="expand">False</property>
250 <property name="fill">True</property>
251 <property name="position">1</property>
252 </packing>
253 </child>
254 <child>
255 <object class="GtkFrame" id="frame_labels">
256 <property name="visible">True</property>
257 <property name="can_focus">False</property>
258 <property name="label_xalign">0</property>
259 <property name="shadow_type">none</property>
260 <child>
261 <object class="GtkAlignment" id="alignment3">
262 <property name="visible">True</property>
263 <property name="can_focus">False</property>
264 <property name="left_padding">12</property>
265 <child>
266 <object class="GtkBox" id="vbox3">
267 <property name="visible">True</property>
268 <property name="can_focus">False</property>
269 <property name="orientation">vertical</property>
270 <child>
271 <object class="GtkLabel" id="label_labeldesc">
272 <property name="visible">True</property>
273 <property name="can_focus">False</property>
274 <property name="xalign">0</property>
275 <property name="label" translatable="yes">Please enter the Labels you want to have
276checked seperated by commas</property>
277 </object>
278 <packing>
279 <property name="expand">False</property>
280 <property name="fill">True</property>
281 <property name="padding">5</property>
282 <property name="position">0</property>
283 </packing>
284 </child>
285 <child>
286 <object class="GtkEntry" id="entry_labels">
287 <property name="visible">True</property>
288 <property name="can_focus">True</property>
289 <property name="invisible_char">●</property>
290 </object>
291 <packing>
292 <property name="expand">False</property>
293 <property name="fill">True</property>
294 <property name="position">1</property>
295 </packing>
296 </child>
297 <child>
298 <object class="GtkCheckButton" id="checkbutton_inbox">
299 <property name="label" translatable="yes">Ignore inbox</property>
300 <property name="visible">True</property>
301 <property name="can_focus">True</property>
302 <property name="receives_default">False</property>
303 <property name="xalign">0.5</property>
304 <property name="yalign">0.46000000834465027</property>
305 <property name="draw_indicator">True</property>
306 </object>
307 <packing>
308 <property name="expand">True</property>
309 <property name="fill">True</property>
310 <property name="position">2</property>
311 </packing>
312 </child>
313 </object>
314 </child>
315 </object>
316 </child>
317 <child type="label">
318 <object class="GtkLabel" id="label_labels">
319 <property name="visible">True</property>
320 <property name="can_focus">False</property>
321 <property name="label" translatable="yes">&lt;b&gt;Labels:&lt;/b&gt;</property>
322 <property name="use_markup">True</property>
323 </object>
324 </child>
325 </object>
326 <packing>
327 <property name="expand">False</property>
328 <property name="fill">True</property>
329 <property name="position">2</property>
330 </packing>
331 </child>
332 <child>
333 <object class="GtkFrame" id="frame_autostart">
334 <property name="visible">True</property>
335 <property name="can_focus">False</property>
336 <property name="label_xalign">0</property>
337 <property name="shadow_type">none</property>
338 <child>
339 <object class="GtkAlignment" id="alignment4">
340 <property name="visible">True</property>
341 <property name="can_focus">False</property>
342 <property name="left_padding">12</property>
343 <child>
344 <object class="GtkCheckButton" id="checkbutton_autostart">
345 <property name="label" translatable="yes">Start automatically on logon</property>
346 <property name="visible">True</property>
347 <property name="can_focus">True</property>
348 <property name="receives_default">False</property>
349 <property name="xalign">0.5</property>
350 <property name="draw_indicator">True</property>
351 </object>
352 </child>
353 </object>
354 </child>
355 <child type="label">
356 <object class="GtkLabel" id="label1">
357 <property name="visible">True</property>
358 <property name="can_focus">False</property>
359 <property name="label" translatable="yes">&lt;b&gt;Autostart:&lt;/b&gt;</property>
360 <property name="use_markup">True</property>
361 </object>
362 </child>
363 </object>
364 <packing>
365 <property name="expand">False</property>
366 <property name="fill">True</property>
367 <property name="position">3</property>
368 </packing>
369 </child>
370 </object>
371 <packing>
372 <property name="position">1</property>
373 <property name="tab_fill">False</property>
374 </packing>
375 </child>
376 <child type="tab">
377 <object class="GtkLabel" id="label_enhanced">
378 <property name="visible">True</property>
379 <property name="can_focus">False</property>
380 <property name="label" translatable="yes">Enhanced</property>
381 </object>
382 <packing>
383 <property name="position">1</property>
384 <property name="tab_fill">False</property>
385 </packing>
386 </child>
387 </object>
388 <packing>
389 <property name="expand">True</property>
390 <property name="fill">True</property>
391 <property name="position">0</property>
392 </packing>
393 </child>
394 <child>
395 <object class="GtkBox" id="hbox_mainbuttons">
396 <property name="visible">True</property>
397 <property name="can_focus">False</property>
398 <property name="spacing">10</property>
399 <child>
400 <object class="GtkButton" id="button_apply">
401 <property name="label">gtk-apply</property>
402 <property name="visible">True</property>
403 <property name="can_focus">True</property>
404 <property name="receives_default">True</property>
405 <property name="use_stock">True</property>
406 <signal name="clicked" handler="on_button_apply_clicked" swapped="no"/>
407 </object>
408 <packing>
409 <property name="expand">True</property>
410 <property name="fill">True</property>
411 <property name="position">0</property>
412 </packing>
413 </child>
414 <child>
415 <object class="GtkButton" id="button_close">
416 <property name="label">gtk-close</property>
417 <property name="visible">True</property>
418 <property name="can_focus">True</property>
419 <property name="receives_default">True</property>
420 <property name="use_stock">True</property>
421 <signal name="clicked" handler="gtk_main_quit" swapped="no"/>
422 </object>
423 <packing>
424 <property name="expand">True</property>
425 <property name="fill">True</property>
426 <property name="position">1</property>
427 </packing>
428 </child>
429 </object>
430 <packing>
431 <property name="expand">False</property>
432 <property name="fill">True</property>
433 <property name="position">1</property>
434 </packing>
435 </child>
436 </object>
437 </child>
438 </object>
439</interface>
0440
=== modified file 'gm-notify'
--- gm-notify 2010-09-18 13:06:25 +0000
+++ gm-notify 2013-03-25 04:23:21 +0000
@@ -1,4 +1,4 @@
1#!/usr/bin/env python1#!/usr/bin/python
2# -*- coding: utf-8 -*-2# -*- coding: utf-8 -*-
33
4# gm-notify v0.10.34# gm-notify v0.10.3
@@ -19,21 +19,18 @@
19# You should have received a copy of the GNU General Public License19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#21#
22from __future__ import print_function
23
22import os24import os
23import sys25import sys
24import subprocess26import subprocess
25import gettext27import gettext
26import webbrowser28import webbrowser
2729
28import pynotify30from gi.repository import Gio, GLib, MessagingMenu, Notify
29import indicate31
30import gobject32from twisted.internet import gireactor
31import pygst33gireactor.install()
32pygst.require("0.10")
33import gst
34import gconf
35from twisted.internet import glib2reactor
36glib2reactor.install()
37from twisted.internet import reactor34from twisted.internet import reactor
38from twisted.words.protocols.jabber import jid35from twisted.words.protocols.jabber import jid
3936
@@ -57,22 +54,69 @@
57 if os.path.exists(path) and os.access(path, os.X_OK): return path54 if os.path.exists(path) and os.access(path, os.X_OK): return path
58 raise PathNotFound("%s not found" % name)55 raise PathNotFound("%s not found" % name)
5956
60class CheckMail():57def play_sound(name):
58 '''Spawns a canberra-gtk-play process to play the sound'''
59 if name is None:
60 return
61 player_path = "/usr/bin/canberra-gtk-play"
62 # Not installed?
63 if not os.path.exists(player_path):
64 return
65 command = [player_path]
66 # File exists, so use the file flag
67 if os.path.exists(name):
68 command.extend(["-f", name])
69 # Assume it is a sound id
70 else:
71 command.extend(["-i", name])
72 try:
73 result = GLib.spawn_async(command)
74 except:
75 return
76 # Does nothing but the documentation says to do it anyway
77 if len(result) > 0:
78 GLib.spawn_close_pid(result[0])
79
80class CheckMail(Gio.Application):
61 def __init__(self):81 def __init__(self):
62 '''initiates DBUS-Messaging interface, creates the MailChecker and registers with indicator-applet.82 '''initiates DBUS-Messaging interface, creates the MailChecker and registers with indicator-applet.
63 In the end it starts the periodic check timer and a gtk main-loop'''83 In the end it starts the periodic check timer and a gtk main-loop'''
64 84 super(CheckMail, self).__init__(application_id="net.launchpad.gm-notify",
65 # Kill running gm-notify processes (UGLY!)85 flags=Gio.ApplicationFlags.FLAGS_NONE)
66 subprocess.call("kill `pgrep -f gm-notify | grep -v %s`" % os.getpid(), stdout=open("/dev/null", "w"), shell=True)86
67 87 self._has_activated = False
68 # Initiate pynotify and Gnome Keyring88 self._counts = {}
69 if not pynotify.init(_("GMail Notifier")):89 self.connect("activate", self.on_activate)
90
91 def on_remote_quit(self, action, args):
92 '''Stops the application when the "remote-quit" action is activated'''
93 reactor.stop()
94
95 def on_activate(self, app):
96 '''When first receiving the activate signal, initialize the primary
97 instance. On subsequent activate signals, we are being activated from a
98 secondary instance, so treat it as if our name was clicked in the
99 messaging menu.'''
100 if self._has_activated:
101 self.indicator_clicked()
102 return
103
104 self._has_activated = True
105
106 # Add an action to quit
107 quit_action = Gio.SimpleAction.new("remote-quit", None)
108 quit_action.connect("activate", self.on_remote_quit)
109 self.add_action(quit_action)
110
111 # Initialize the desktop notifications
112 if not Notify.init(_("GMail Notifier")):
70 sys.exit(-1)113 sys.exit(-1)
71 114
72 keys = keyring.Keyring("GMail", "mail.google.com", "http")115 keys = keyring.Keyring("GMail", "mail.google.com", "http")
73 if keys.has_credentials():116 if keys.has_credentials():
74 self.creds = keys.get_credentials()117 self.creds = keys.get_credentials()
75 else:118 else:
119 print("Failed to get credentials")
76 # Start gm-notify-config if no credentials are found120 # Start gm-notify-config if no credentials are found
77 try:121 try:
78 subprocess.call(get_executable_path("gm-notify-config"))122 subprocess.call(get_executable_path("gm-notify-config"))
@@ -88,67 +132,86 @@
88 else:132 else:
89 self.domain = self.jid.host133 self.domain = self.jid.host
90 134
91 # init gconf to read config values135 self.client = Gio.Settings("net.launchpad.gm-notify")
92 self.client = gconf.client_get_default()136
93 137 # Set up the sound file
94 # init sound138 self._soundfile = self.client.get_string("soundfile")
95 soundfile = self.client.get_string("/apps/gm-notify/soundfile")139 if self._soundfile == '':
96 if self.client.get_bool("/apps/gm-notify/play_sound") and soundfile:140 self._soundfile = "message-new-instant"
97 self.player = gst.element_factory_make("playbin", "player")141 if not self.client.get_boolean("play-sound"):
98 self.player.set_property("video-sink", gst.element_factory_make("fakesink", "fakesink"))142 self._soundfile = None
99 self.player.set_property("uri", "file://" + soundfile)143
100 bus = self.player.get_bus()144 # Messaging Menu integration
101 bus.add_signal_watch()145 self._m_menu = MessagingMenu.App.new("gm-notify.desktop")
102 bus.connect("message", self.gst_message)146 self._m_menu.register()
103 else:147 self._m_menu.connect("activate-source", self.source_clicked)
104 self.player = None148
105 149 # Read ignore-inbox value. If true you will only receive notifications
106 # Register with Indicator-Applet150 # about configured labels
107 self.server = indicate.indicate_server_ref_default()151 self.ignore_inbox = self.client.get_boolean("ignore-inbox")
108 self.server.set_type("message.mail")
109 self.server.set_desktop_file("/usr/share/gm-notify/gm-notify.desktop")
110 self.server.connect("server-display", self.serverClick)
111 self.indicators = {}
112
113 # Read /apps/gm-notify/ignore_inbox value. If true you will only receive
114 # notifications about configured labels
115 self.ignore_inbox = self.client.get_bool("/apps/gm-notify/ignore_inbox")
116 152
117 # Retrieve the mailbox we're gonna check153 # Retrieve the mailbox we're gonna check
118 self.mailboxes = self.client.get_list("/apps/gm-notify/mailboxes", gconf.VALUE_STRING)154 self.mailboxes = self.client.get_strv("mailboxes")
119 self.mailboxes.insert(0, "inbox")155 self.mailboxes.insert(0, "inbox")
120 self.addMailboxIndicators()
121 self.checker = MailChecker(self.jid, self.creds[1], self.mailboxes[1:], self.new_mail, self.update_count)156 self.checker = MailChecker(self.jid, self.creds[1], self.mailboxes[1:], self.new_mail, self.update_count)
122 self.checker.connect()157 self.checker.connect()
123 158
124 reactor.run()159 def indicator_clicked(self):
125 160 '''called when "Google Mail" is clicked in indicator-messages and
126 def gst_message(self, bus, message):161 performs a Mail Check'''
127 if message.type == gst.MESSAGE_EOS:162 for label in self.mailboxes:
128 self.player.set_state(gst.STATE_NULL)163 self.remove_attention(label)
129 elif message.type == gst.MESSAGE_ERROR:164
130 self.player.set_state(gst.STATE_NULL)
131 print "Error: %s - %s" % message.parse_error()
132
133 def serverClick(self, server, timestamp=None):
134 '''called when the server is clicked in the indicator-applet and performs a Mail Check'''
135 for indicator in self.indicators:
136 self.indicators[indicator].set_property("draw-attention", "false")
137
138 if self.player: self.player.set_state(gst.STATE_NULL)
139 self.checker.queryInbox()165 self.checker.queryInbox()
166
167 def remove_attention(self, label):
168 '''Removes attention from the label source if it exists'''
169 if self._m_menu.has_source(label):
170 self._m_menu.remove_attention(label)
171
172 def has_source(self, label):
173 '''Returns true if we have this label, or if we don't and it is in our
174 mailboxes list, create it'''
175 if label == "inbox" and self.ignore_inbox:
176 return False
177 elif label in self.mailboxes:
178 if not self._m_menu.has_source(label):
179 if label in MAILBOXES_NAMES:
180 name = MAILBOXES_NAMES[label]
181 else:
182 name = label
183 if label == "inbox":
184 self._m_menu.insert_source_with_string(0, label, None, name, _("empty"))
185 else:
186 self._m_menu.append_source_with_string(label, None, name, _("empty"))
187 if label in self._counts:
188 self._m_menu.set_source_count(label, self._counts[label])
189 return True
190 else:
191 return False
140 192
141 def update_count(self, count):193 def update_count(self, count):
194 '''Updates the count for all the mailboxes'''
142 for mailbox in count.iteritems():195 for mailbox in count.iteritems():
143 if mailbox[0] == "inbox" and self.ignore_inbox:196 if mailbox[0] == "inbox" and self.ignore_inbox:
144 continue197 continue
145 198
146 i = self.indicators[mailbox[0]]199 if self.has_source(mailbox[0]):
147 if int(i.get_property("count")) > int(mailbox[1]):200 # Get the last count
148 i.set_property("draw-attention", "false")201 last_count = 0
149 i.set_property("count", unicode(mailbox[1]))202 if mailbox[0] in self._counts:
150 if int(mailbox[1]) or mailbox[0] == "inbox": i.show()203 last_count = self._counts[mailbox[0]]
151 else: i.hide()204 current_count = int(mailbox[1])
205
206 # Remove attention if the count has decreased
207 if last_count > current_count:
208 self._m_menu.remove_attention(mailbox[0])
209 if current_count > 0:
210 self._m_menu.set_source_count(mailbox[0], current_count)
211 # Remove the source if 0 messages, to save space
212 else:
213 self._m_menu.remove_source(mailbox[0])
214 self._counts[mailbox[0]] = current_count
152 215
153 def new_mail(self, mails):216 def new_mail(self, mails):
154 '''Takes mailbox name and titles of mails, to display notification and add indicators'''217 '''Takes mailbox name and titles of mails, to display notification and add indicators'''
@@ -158,11 +221,12 @@
158 got_label = False221 got_label = False
159 for label in mail['labels']:222 for label in mail['labels']:
160 if label == u"^i": label = "inbox"223 if label == u"^i": label = "inbox"
161 if label in self.indicators:224 if label == "inbox" and self.ignore_inbox:
162 if not label == "inbox" or not self.ignore_inbox:225 continue
163 got_label = True226 if self.has_source(label):
164 self.indicators[label].set_property("draw-attention", "true")227 got_label = True
165 if not got_label and self.ignore_inbox: continue228 self._m_menu.draw_attention(label)
229 if not got_label: continue
166 230
167 if "sender_name" in mail: text += mail['sender_name'] + ":\n"231 if "sender_name" in mail: text += mail['sender_name'] + ":\n"
168 elif "sender_address" in mail: text += mail['sender_address'] + ":\n"232 elif "sender_address" in mail: text += mail['sender_address'] + ":\n"
@@ -179,9 +243,9 @@
179 243
180 if text:244 if text:
181 self.showNotification(_("Incoming message"), text.strip("\n"))245 self.showNotification(_("Incoming message"), text.strip("\n"))
182 if self.player: self.player.set_state(gst.STATE_PLAYING)246 play_sound(self._soundfile)
183 247
184 def labelClick(self, indicator, timestamp=None):248 def source_clicked(self, app, source_id):
185 '''called when a label is clicked in the indicator-applet and opens the corresponding gmail page'''249 '''called when a label is clicked in the indicator-applet and opens the corresponding gmail page'''
186 if self.domain:250 if self.domain:
187 url = "https://mail.google.com/a/"+self.domain+"/"251 url = "https://mail.google.com/a/"+self.domain+"/"
@@ -189,49 +253,29 @@
189 url = "https://mail.google.com/mail/"253 url = "https://mail.google.com/mail/"
190 254
191 try:255 try:
192 url += "#%s" % MAILBOXES_URLS[indicator.label]256 url += "#%s" % MAILBOXES_URLS[source_id]
193 except KeyError:257 except KeyError:
194 url += "#label/%s" % indicator.label258 url += "#label/%s" % source_id
195
196 indicator.set_property("draw-attention", "false")
197 259
198 # Open mail client260 # Open mail client
199 if self.client.get_bool("/apps/gm-notify/openclient"):261 if self.client.get_boolean("openclient"):
200 command = self.client.get_string("/desktop/gnome/url-handlers/mailto/command").split(" ")[0]262 try:
201 if command.find("mutt") != -1: command += " -f =%s" % indicator.label263 info = Gio.AppInfo.get_default_for_type("x-scheme-handler/mailto", False)
202 if self.client.get_bool("/desktop/gnome/url-handlers/mailto/needs_terminal"):264 info.launch(None, None)
203 termCmd = self.client.get_string("/desktop/gnome/applications/terminal/exec")265 except:
204 if termCmd:266 pass
205 termCmd += " " + self.client.get_string("/desktop/gnome/applications/terminal/exec_arg") + " "
206 else:
207 termCmd = "gnome-terminal -x "
208 command = termCmd + command
209 subprocess.Popen(command, shell=True)
210 else:267 else:
211 webbrowser.open(url)268 webbrowser.open(url)
212 269
213 def showNotification(self, title, message):270 def showNotification(self, title, message):
214 '''takes a title and a message to display the email notification. Returns the271 '''takes a title and a message to display the email notification. Returns the
215 created notification object'''272 created notification object'''
216 273
217 n = pynotify.Notification(title, message, "notification-message-email")274 n = Notify.Notification.new(title, message, "notification-message-email")
218 n.show()275 n.show()
219 276
220 return n277 return n
221 278
222 def addMailboxIndicators(self):
223 for mailbox in reversed(self.mailboxes):
224 new_indicator = indicate.Indicator()
225
226 try:
227 new_indicator.set_property("name", MAILBOXES_NAMES[mailbox])
228 except KeyError:
229 new_indicator.set_property("name", mailbox)
230 new_indicator.set_property("count", "0")
231 new_indicator.label = mailbox
232 new_indicator.connect("user-display", self.labelClick)
233 self.indicators[mailbox] = new_indicator
234 self.indicators["inbox"].show()
235 if self.ignore_inbox: self.indicators["inbox"].hide()
236
237cm = CheckMail()279cm = CheckMail()
280reactor.registerGApplication(cm)
281reactor.run()
238282
=== modified file 'gm-notify-config'
--- gm-notify-config 2010-09-18 13:06:25 +0000
+++ gm-notify-config 2013-03-25 04:23:21 +0000
@@ -19,19 +19,18 @@
19# You should have received a copy of the GNU General Public License19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#21#
22from __future__ import print_function
23
22import sys24import sys
23import os25import os
24import gettext26import gettext
25import subprocess27import subprocess
26import shutil28import shutil
2729
28import pynotify30from gi.repository import Gio, Gtk
29import pygtk31
30pygtk.require("2.0")32from twisted.internet import gtk3reactor
31import gtk, gtk.glade33gtk3reactor.install()
32import gconf
33from twisted.internet import gtk2reactor
34gtk2reactor.install()
35from twisted.internet import reactor34from twisted.internet import reactor
36from twisted.words.protocols.jabber import jid35from twisted.words.protocols.jabber import jid
3736
@@ -39,8 +38,6 @@
39from gtalk import MailChecker38from gtalk import MailChecker
4039
41_ = gettext.translation('gm-notify', fallback=True).ugettext40_ = gettext.translation('gm-notify', fallback=True).ugettext
42if not pynotify.init(_("GMail Notifier")):
43 sys.exit(-1)
4441
45class PathNotFound(Exception): pass42class PathNotFound(Exception): pass
4643
@@ -53,24 +50,39 @@
53 if os.path.exists(path) and os.access(path, os.X_OK): return path50 if os.path.exists(path) and os.access(path, os.X_OK): return path
54 raise PathNotFound("%s not found" % name)51 raise PathNotFound("%s not found" % name)
5552
56class Window:53class Window(Gtk.Application):
57 def __init__(self):54 def __init__(self):
55 super(Window, self).__init__(application_id="net.launchpad.gm-notify-config",
56 flags=Gio.ApplicationFlags.FLAGS_NONE)
57 self.window = None
58 self.connect("activate", self.on_activate)
59
60 def on_activate(self, app):
61 '''Setup the application'''
62 if self.window is not None:
63 return
64
58 #####65 #####
59 # GUI initialization66 # GUI initialization
60 #####67 #####
61 self.keys = keyring.Keyring("GMail", "mail.google.com", "http")68 self.keys = keyring.Keyring("GMail", "mail.google.com", "http")
62 self.client = gconf.client_get_default()69 self.client = Gio.Settings("net.launchpad.gm-notify")
63 70
64 if os.path.exists("gm-config.glade"):71 if os.path.exists("gm-config.ui"):
65 glade_file = "gm-config.glade"72 builder_file = "gm-config.ui"
66 elif os.path.exists("/usr/local/share/gm-notify/gm-config.glade"):73 elif os.path.exists("/usr/local/share/gm-notify/gm-config.ui"):
67 glade_file = "/usr/local/share/gm-notify/gm-config.glade"74 builder_file = "/usr/local/share/gm-notify/gm-config.ui"
68 elif os.path.exists("/usr/share/gm-notify/gm-config.glade"):75 elif os.path.exists("/usr/share/gm-notify/gm-config.ui"):
69 glade_file = "/usr/share/gm-notify/gm-config.glade"76 builder_file = "/usr/share/gm-notify/gm-config.ui"
7077
71 self.wTree = gtk.glade.XML(glade_file, "gmnotify_config_main", "gm-notify")78 self.wTree = Gtk.Builder.new()
72 self.window = self.wTree.get_widget("gmnotify_config_main")79 self.wTree.add_from_file(builder_file)
80 self.wTree.set_translation_domain("gm-notify")
81 self.window = self.wTree.get_object("gmnotify_config_main")
73 self.window.show_all()82 self.window.show_all()
83 self.add_window(self.window)
84
85 self.wTree.get_object("notebook_main").set_current_page(0)
74 86
75 #####87 #####
76 # Init with stored values88 # Init with stored values
@@ -81,8 +93,8 @@
81 self.creds = self.keys.get_credentials()93 self.creds = self.keys.get_credentials()
82 else:94 else:
83 self.creds = ("", "")95 self.creds = ("", "")
84 self.wTree.get_widget("input_user").set_text(self.creds[0])96 self.wTree.get_object("input_user").set_text(self.creds[0])
85 self.wTree.get_widget("input_password").set_text(self.creds[1])97 self.wTree.get_object("input_password").set_text(self.creds[1])
86 98
87 self.api = MailChecker("", "")99 self.api = MailChecker("", "")
88 self.api.cb_auth_successful = self.credentials_valid100 self.api.cb_auth_successful = self.credentials_valid
@@ -91,68 +103,64 @@
91 self.check_credentials(None, None)103 self.check_credentials(None, None)
92 104
93 # Sound105 # Sound
94 self.wTree.get_widget("checkbutton_sound").set_active(self.client.get_bool("/apps/gm-notify/play_sound"))106 self.wTree.get_object("checkbutton_sound").set_active(self.client.get_boolean("play-sound"))
95 if self.client.get_string("/apps/gm-notify/soundfile"):107 if self.client.get_string("soundfile"):
96 self.wTree.get_widget("fcbutton_sound").set_filename(self.client.get_string("/apps/gm-notify/soundfile"))108 self.wTree.get_object("fcbutton_sound").set_filename(self.client.get_string("soundfile"))
97 self.on_checkbutton_sound_toggled(self.wTree.get_widget("checkbutton_sound"))109 self.on_checkbutton_sound_toggled(self.wTree.get_object("checkbutton_sound"))
98 110
99 # ClickAction111 # ClickAction
100 if self.client.get_bool("/apps/gm-notify/openclient"):112 if self.client.get_boolean("openclient"):
101 self.wTree.get_widget("radiobutton_openclient").set_active(True)113 self.wTree.get_object("radiobutton_openclient").set_active(True)
102 else:114 else:
103 self.wTree.get_widget("radiobutton_openweb").set_active(True)115 self.wTree.get_object("radiobutton_openweb").set_active(True)
104 116
105 # Mailboxes117 # Mailboxes
106 mailboxes = self.client.get_list("/apps/gm-notify/mailboxes", gconf.VALUE_STRING)118 mailboxes = self.client.get_strv("mailboxes")
107 self.wTree.get_widget("checkbutton_inbox").set_active(self.client.get_bool("/apps/gm-notify/ignore_inbox"))119 self.wTree.get_object("checkbutton_inbox").set_active(self.client.get_boolean("ignore-inbox"))
108 self.wTree.get_widget("entry_labels").set_text(", ".join(mailboxes))120 self.wTree.get_object("entry_labels").set_text(", ".join(mailboxes))
109121
110 # Autorun122 # Autorun
111 if os.path.exists("data/gm-notify.desktop"):123 if os.path.exists("data/gm-notify.desktop"):
112 self.gm_notify_autostart_file = "data/gm-notify.desktop"124 self.gm_notify_autostart_file = "data/gm-notify.desktop"
113 elif os.path.exists("/usr/local/share/gm-notify/gm-notify.desktop"):125 elif os.path.exists("/usr/local/share/applications/gm-notify.desktop"):
114 self.gm_notify_autostart_file = "/usr/local/share/gm-notify/gm-notify.desktop"126 self.gm_notify_autostart_file = "/usr/local/share/applications/gm-notify.desktop"
115 elif os.path.exists("/usr/share/gm-notify/gm-notify.desktop"):127 elif os.path.exists("/usr/share/applications/gm-notify.desktop"):
116 self.gm_notify_autostart_file = "/usr/share/gm-notify/gm-notify.desktop"128 self.gm_notify_autostart_file = "/usr/share/applications/gm-notify.desktop"
117 self.autostart_file = os.path.expanduser("~/.config/autostart/gm-notify.desktop")129 self.autostart_file = os.path.expanduser("~/.config/autostart/gm-notify.desktop")
118 self.wTree.get_widget("checkbutton_autostart").set_active(os.path.exists(self.autostart_file))130 self.wTree.get_object("checkbutton_autostart").set_active(os.path.exists(self.autostart_file))
119 131
120 signals = { "gtk_main_quit": self.terminate,132 # Connect signals
121 "on_button_apply_clicked": self.save,133 self.wTree.get_object("button_close").connect("clicked", self.terminate)
122 "on_input_password_focus_out_event": self.check_credentials,134 self.wTree.get_object("button_apply").connect("clicked", self.save)
123 "on_checkbutton_sound_toggled": self.on_checkbutton_sound_toggled,135 self.wTree.get_object("input_password").connect("focus-out-event", self.check_credentials)
124 }136 self.wTree.get_object("checkbutton_sound").connect("toggled", self.on_checkbutton_sound_toggled)
125 self.wTree.signal_autoconnect(signals)
126 137
127 def save(self, widget, data=None):138 def save(self, widget, data=None):
128 '''saves the entered data and closes the app'''139 '''saves the entered data and closes the app'''
129
130 self.client.add_dir("/apps/gm-notify", gconf.CLIENT_PRELOAD_NONE)
131
132 # Credentials140 # Credentials
133 self.keys.delete_credentials()141 self.keys.delete_credentials()
134 self.keys.set_credentials(( self.wTree.get_widget("input_user").get_text(), 142 self.keys.set_credentials(self.wTree.get_object("input_user").get_text(),
135 self.wTree.get_widget("input_password").get_text()))143 self.wTree.get_object("input_password").get_text())
136 144
137 # Mailboxes145 # Mailboxes
138 mailboxes = []146 mailboxes = []
139 for label in self.wTree.get_widget("entry_labels").get_text().split(","):147 for label in self.wTree.get_object("entry_labels").get_text().split(","):
140 mailboxes.append(label.strip())148 mailboxes.append(label.strip())
141 self.client.set_list("/apps/gm-notify/mailboxes", gconf.VALUE_STRING, mailboxes)149 self.client.set_strv("mailboxes", mailboxes)
142 self.client.set_bool("/apps/gm-notify/ignore_inbox", self.wTree.get_widget("checkbutton_inbox").get_active())150 self.client.set_boolean("ignore-inbox", self.wTree.get_object("checkbutton_inbox").get_active())
143 151
144 # ClickAction152 # ClickAction
145 self.client.set_bool("/apps/gm-notify/openclient", self.wTree.get_widget("radiobutton_openclient").get_active())153 self.client.set_boolean("openclient", self.wTree.get_object("radiobutton_openclient").get_active())
146154
147 # Soundfile155 # Soundfile
148 if self.wTree.get_widget("checkbutton_sound").get_active() and self.wTree.get_widget("fcbutton_sound").get_filename():156 if self.wTree.get_object("checkbutton_sound").get_active() and self.wTree.get_object("fcbutton_sound").get_filename():
149 self.client.set_bool("/apps/gm-notify/play_sound", True)157 self.client.set_boolean("play-sound", True)
150 self.client.set_string("/apps/gm-notify/soundfile", str(self.wTree.get_widget("fcbutton_sound").get_filename()))158 self.client.set_string("soundfile", str(self.wTree.get_object("fcbutton_sound").get_filename()))
151 else:159 else:
152 self.client.set_bool("/apps/gm-notify/play_sound", False)160 self.client.set_boolean("play-sound", False)
153161
154 # Autorun162 # Autorun
155 if self.wTree.get_widget("checkbutton_autostart").get_active():163 if self.wTree.get_object("checkbutton_autostart").get_active():
156 if not os.path.exists(self.autostart_file):164 if not os.path.exists(self.autostart_file):
157 try:165 try:
158 os.makedirs(os.path.expanduser("~/.config/autostart"))166 os.makedirs(os.path.expanduser("~/.config/autostart"))
@@ -163,13 +171,13 @@
163 shutil.copyfile(self.gm_notify_autostart_file, self.autostart_file)171 shutil.copyfile(self.gm_notify_autostart_file, self.autostart_file)
164 os.chmod(self.autostart_file, 0700)172 os.chmod(self.autostart_file, 0700)
165 except IOError:173 except IOError:
166 print "Warning: cannot write to path", self.autostart_file174 print("Warning: cannot write to path", self.autostart_file)
167 else:175 else:
168 if os.path.exists(self.autostart_file):176 if os.path.exists(self.autostart_file):
169 try:177 try:
170 os.unlink(self.autostart_file)178 os.unlink(self.autostart_file)
171 except:179 except:
172 print "Warning: cannot delete", self.autostart_file180 print("Warning: cannot delete", self.autostart_file)
173181
174 # Start gm-notify itself182 # Start gm-notify itself
175 subprocess.Popen(get_executable_path("gm-notify"))183 subprocess.Popen(get_executable_path("gm-notify"))
@@ -178,16 +186,16 @@
178 reactor.stop()186 reactor.stop()
179 187
180 def on_checkbutton_sound_toggled(self, widget):188 def on_checkbutton_sound_toggled(self, widget):
181 self.wTree.get_widget("fcbutton_sound").set_sensitive(self.wTree.get_widget("checkbutton_sound").get_active())189 self.wTree.get_object("fcbutton_sound").set_sensitive(self.wTree.get_object("checkbutton_sound").get_active())
182 190
183 def check_credentials(self, widget, event, data=None):191 def check_credentials(self, widget, event, data=None):
184 '''check if the given credentials are valid'''192 '''check if the given credentials are valid'''
185 193
186 input_user = self.wTree.get_widget("input_user")194 input_user = self.wTree.get_object("input_user")
187 input_password = self.wTree.get_widget("input_password")195 input_password = self.wTree.get_object("input_password")
188 image_credentials = self.wTree.get_widget("image_credentials")196 image_credentials = self.wTree.get_object("image_credentials")
189 label_credentials = self.wTree.get_widget("label_credentials")197 label_credentials = self.wTree.get_object("label_credentials")
190 button_apply = self.wTree.get_widget("button_apply")198 button_apply = self.wTree.get_object("button_apply")
191 button_apply.set_sensitive(False)199 button_apply.set_sensitive(False)
192 200
193 # Change status text and disable input fields201 # Change status text and disable input fields
@@ -203,13 +211,13 @@
203 return False211 return False
204 212
205 def credentials_valid(self):213 def credentials_valid(self):
206 input_user = self.wTree.get_widget("input_user")214 input_user = self.wTree.get_object("input_user")
207 input_password = self.wTree.get_widget("input_password")215 input_password = self.wTree.get_object("input_password")
208 image_credentials = self.wTree.get_widget("image_credentials")216 image_credentials = self.wTree.get_object("image_credentials")
209 label_credentials = self.wTree.get_widget("label_credentials")217 label_credentials = self.wTree.get_object("label_credentials")
210 button_apply = self.wTree.get_widget("button_apply")218 button_apply = self.wTree.get_object("button_apply")
211 219
212 image_credentials.set_from_icon_name("gtk-yes", gtk.ICON_SIZE_MENU)220 image_credentials.set_from_icon_name("gtk-yes", Gtk.IconSize.MENU)
213 label_credentials.set_text(_("Valid credentials"))221 label_credentials.set_text(_("Valid credentials"))
214 button_apply.set_sensitive(True)222 button_apply.set_sensitive(True)
215 input_user.set_sensitive(True)223 input_user.set_sensitive(True)
@@ -218,12 +226,12 @@
218 self.api.die()226 self.api.die()
219 227
220 def credentials_invalid(self):228 def credentials_invalid(self):
221 input_user = self.wTree.get_widget("input_user")229 input_user = self.wTree.get_object("input_user")
222 input_password = self.wTree.get_widget("input_password")230 input_password = self.wTree.get_object("input_password")
223 image_credentials = self.wTree.get_widget("image_credentials")231 image_credentials = self.wTree.get_object("image_credentials")
224 label_credentials = self.wTree.get_widget("label_credentials")232 label_credentials = self.wTree.get_object("label_credentials")
225 233
226 image_credentials.set_from_icon_name("gtk-stop", gtk.ICON_SIZE_MENU)234 image_credentials.set_from_icon_name("gtk-stop", Gtk.IconSize.MENU)
227 label_credentials.set_text(_("Invalid credentials"))235 label_credentials.set_text(_("Invalid credentials"))
228 input_user.set_sensitive(True)236 input_user.set_sensitive(True)
229 input_password.set_sensitive(True)237 input_password.set_sensitive(True)
@@ -231,4 +239,5 @@
231 self.api.die()239 self.api.die()
232240
233t = Window()241t = Window()
242reactor.registerGApplication(t)
234reactor.run()243reactor.run()
235244
=== modified file 'gm_notify_keyring.py'
--- gm_notify_keyring.py 2010-05-05 17:31:35 +0000
+++ gm_notify_keyring.py 2013-03-25 04:23:21 +0000
@@ -1,43 +1,59 @@
1from __future__ import print_function
2
1__version__ = "$Revision: 14294 $"3__version__ = "$Revision: 14294 $"
24
3import gtk # ensure that the application name is correctly set5from gi.repository import GnomeKeyring, Gtk
4import gnomekeyring as gkey6
57def attributes(d):
8 '''Converts a dictionary to a GnomeKeyring.Attribute array'''
9 attrs = GnomeKeyring.Attribute.list_new()
10 for key in d:
11 GnomeKeyring.Attribute.list_append_string(attrs, key, str(d[key]))
12 return attrs
13
14def dict_from_attributes(attrs):
15 '''Converts item results back into a dictionary'''
16 result = {}
17 for attr in GnomeKeyring.Attribute.list_to_glist(attrs):
18 result[attr.name] = attr.get_string()
19 return result
20
21class KeyringException(Exception):
22 pass
623
7class Keyring(object):24class Keyring(object):
8 def __init__(self, name, server, protocol):25 def __init__(self, name, server, protocol):
9 self._name = name26 self._name = name
10 self._server = server27 self._server = server
11 self._protocol = protocol28 self._protocol = protocol
12 self._keyring = gkey.get_default_keyring_sync()29 result, self._keyring = GnomeKeyring.get_default_keyring_sync()
13 30
14 def has_credentials(self):31 def has_credentials(self):
15 try:32 attrs = attributes({"server": self._server, "protocol": self._protocol})
16 attrs = {"server": self._server, "protocol": self._protocol}33 result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs)
17 items = gkey.find_items_sync(gkey.ITEM_NETWORK_PASSWORD, attrs)34 if result in (GnomeKeyring.Result.NO_MATCH, GnomeKeyring.Result.DENIED):
18 return len(items) > 0
19 except (gkey.DeniedError, gkey.NoMatchError):
20 return False35 return False
36 return len(items) > 0
2137
22 def get_credentials(self):38 def get_credentials(self):
23 attrs = {"server": self._server, "protocol": self._protocol}39 attrs = attributes({"server": self._server, "protocol": self._protocol})
24 items = gkey.find_items_sync(gkey.ITEM_NETWORK_PASSWORD, attrs)40 result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs)
25 return (items[0].attributes["user"], items[0].secret)41 if len(items) == 0:
42 raise KeyringException("Credentials not found")
43 d = dict_from_attributes(items[0].attributes)
44 return (d["user"], items[0].secret)
26 45
27 def delete_credentials(self):46 def delete_credentials(self):
28 attrs = {"server": self._server, "protocol": self._protocol}47 attrs = attributes({"server": self._server, "protocol": self._protocol})
29 try:48 result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs)
30 items = gkey.find_items_sync(gkey.ITEM_NETWORK_PASSWORD, attrs)49 for item in items:
31 for item in items:50 GnomeKeyring.item_delete_sync(self._keyring, item.item_id)
32 gkey.item_delete_sync(None, item.item_id)
33 except (gkey.DeniedError, gkey.NoMatchError):
34 pass
35 51
36 def set_credentials(self, (user, pw)):52 def set_credentials(self, user, pw):
37 attrs = {53 attrs = attributes({
38 "user": user,54 "user": user,
39 "server": self._server,55 "server": self._server,
40 "protocol": self._protocol,56 "protocol": self._protocol,
41 }57 })
42 gkey.item_create_sync(gkey.get_default_keyring_sync(),58 GnomeKeyring.item_create_sync(self._keyring,
43 gkey.ITEM_NETWORK_PASSWORD, self._name, attrs, pw, True)59 GnomeKeyring.ItemType.NETWORK_PASSWORD, self._name, attrs, pw, True)
4460
=== modified file 'gtalk.py'
--- gtalk.py 2010-09-18 13:06:25 +0000
+++ gtalk.py 2013-03-25 04:23:21 +0000
@@ -19,17 +19,19 @@
19# You should have received a copy of the GNU General Public License19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#21#
22from __future__ import print_function
23
22from threading import Event24from threading import Event
2325
24from twisted.words.protocols.jabber import xmlstream, client, jid26from twisted.words.protocols.jabber import xmlstream, client, jid
25from twisted.words.xish import domish27from twisted.words.xish import domish
26from twisted.internet import reactor, task28from twisted.internet import reactor, task, error
2729
28_DEBUG = False30_DEBUG = False
29COLOR_GREEN = "\033[92m"31COLOR_GREEN = "\033[92m"
30COLOR_END = "\033[0m"32COLOR_END = "\033[0m"
31def DEBUG(msg):33def DEBUG(msg):
32 if _DEBUG: print COLOR_GREEN + str(msg) + COLOR_END34 if _DEBUG: print(COLOR_GREEN + str(msg) + COLOR_END)
3335
34class GTalkClientFactory(xmlstream.XmlStreamFactory):36class GTalkClientFactory(xmlstream.XmlStreamFactory):
35 def __init__(self, jid, password):37 def __init__(self, jid, password):
@@ -89,7 +91,11 @@
89 self.connector.disconnect() # Our reconnecting factory will try the reconnecting91 self.connector.disconnect() # Our reconnecting factory will try the reconnecting
9092
91 def send_callback_handler(self, data, callback=None, **kargs):93 def send_callback_handler(self, data, callback=None, **kargs):
92 self.timeout_call_id.cancel()94 try:
95 self.timeout_call_id.cancel()
96 except error.AlreadyCalled:
97 DEBUG("already called timeout_call_id.cancel()")
98 return
93 if callback:99 if callback:
94 callback(data, **kargs)100 callback(data, **kargs)
95 else:101 else:
@@ -242,10 +248,10 @@
242 if iq: self.queryInbox()248 if iq: self.queryInbox()
243 249
244 def rawDataIn(self, buf):250 def rawDataIn(self, buf):
245 print u"< %s" % unicode(buf, "utf-8")251 print(u"< %s" % unicode(buf, "utf-8"))
246 252
247 def rawDataOut(self, buf):253 def rawDataOut(self, buf):
248 print u"> %s" % unicode(buf, "utf-8")254 print(u"> %s" % unicode(buf, "utf-8"))
249 255
250 def connectedCB(self, xmlstream):256 def connectedCB(self, xmlstream):
251 self.xmlstream = xmlstream257 self.xmlstream = xmlstream
252258
=== modified file 'setup.py'
--- setup.py 2010-09-18 13:06:25 +0000
+++ setup.py 2013-03-25 04:23:21 +0000
@@ -10,11 +10,10 @@
10 py_modules=['gtalk', 'gm_notify_keyring'],10 py_modules=['gtalk', 'gm_notify_keyring'],
11 scripts=['gm-notify', 'gm-notify-config'],11 scripts=['gm-notify', 'gm-notify-config'],
12 data_files=[('/usr/share/applications', ['data/gm-notify-config.desktop']),12 data_files=[('/usr/share/applications', ['data/gm-notify-config.desktop']),
13 ('/usr/share/indicators/messages/applications', ['data/gm-notify']),13 ('/usr/share/applications', ['data/gm-notify.desktop']),
14 ('/usr/share/gm-notify', ['data/gm-notify.desktop']),
15 ('/usr/share/gm-notify', ['data/checking.gif']),14 ('/usr/share/gm-notify', ['data/checking.gif']),
16 ('/usr/share/gm-notify', ['gm-config.glade']),15 ('/usr/share/gm-notify', ['gm-config.ui']),
17 ('/etc/gconf/schemas', ['data/gm-notify.schemas']),16 ('/usr/share/glib-2.0/schemas', ['data/net.launchpad.gm-notify.gschema.xml']),
18 ('/usr/share/locale/da/LC_MESSAGES', ['po/da/gm-notify.mo']),17 ('/usr/share/locale/da/LC_MESSAGES', ['po/da/gm-notify.mo']),
19 ('/usr/share/locale/bg/LC_MESSAGES', ['po/bg/gm-notify.mo']),18 ('/usr/share/locale/bg/LC_MESSAGES', ['po/bg/gm-notify.mo']),
20 ('/usr/share/locale/de/LC_MESSAGES', ['po/de/gm-notify.mo']),19 ('/usr/share/locale/de/LC_MESSAGES', ['po/de/gm-notify.mo']),

Subscribers

People subscribed via source and target branches

to status/vote changes: