3636from tkinter .messagebox import showerror , askokcancel
3737from tkinter .ttk import Entry , Label , Button , Style
3838from PIL import Image , ImageDraw , ImageFont
39- from checkmailslib .tktray import Icon
40- from checkmailslib .constants import IMAGE , ICON , IMAGE2 , save_config , ICON_48
41- from checkmailslib . constants import encrypt , decrypt , LOCAL_PATH , CONFIG , internet_on
39+ from checkmailslib .trayicon import TrayIcon
40+ from checkmailslib .constants import IMAGE , ICON , IMAGE2 , save_config , FONTSIZE ,\
41+ encrypt , decrypt , LOCAL_PATH , CONFIG , internet_on , TTF_FONTS , ICON_48
4242from checkmailslib .manager import Manager
4343from checkmailslib .config import Config
4444from checkmailslib .about import About
@@ -54,33 +54,41 @@ def __init__(self):
5454 # icon that will show up in the taskbar for every toplevel
5555 self .im_icon = PhotoImage (master = self , file = ICON_48 )
5656 self .iconphoto (True , self .im_icon )
57+
5758 # system tray icon
58- self .img = PhotoImage ( file = IMAGE )
59- self .icon = Icon ( self , image = self .img )
60- self .icon .menu . add_command (label = _ ("Check" ), command = self .check_mails )
61- self .icon .menu . add_command (label = _ ("Reconnect" ),
59+ self .icon = TrayIcon ( IMAGE )
60+ self .icon . add_menu_item ( label = _ ( "Details" ), command = self .display )
61+ self .icon .add_menu_item (label = _ ("Check" ), command = self .check_mails )
62+ self .icon .add_menu_item (label = _ ("Reconnect" ),
6263 command = self .reconnect )
63- self .icon .menu . add_command (label = _ ("Suspend" ), command = self .start_stop )
64- self .icon .menu . add_separator ()
65- self .icon .menu . add_command (label = _ ("Change password" ),
64+ self .icon .add_menu_item (label = _ ("Suspend" ), command = self .start_stop )
65+ self .icon .add_menu_separator ()
66+ self .icon .add_menu_item (label = _ ("Change password" ),
6667 command = self .change_password )
67- self .icon .menu . add_command (label = _ ("Reset password" ),
68+ self .icon .add_menu_item (label = _ ("Reset password" ),
6869 command = self .reset_password )
69- self .icon .menu . add_separator ()
70- self .icon .menu . add_command (label = _ ("Manage mailboxes" ),
70+ self .icon .add_menu_separator ()
71+ self .icon .add_menu_item (label = _ ("Manage mailboxes" ),
7172 command = self .manage_mailboxes )
72- self .icon .menu . add_command (label = _ ("Preferences" ), command = self .config )
73- self .icon .menu . add_separator ()
74- self .icon .menu . add_command (label = _ ("Check for updates" ),
73+ self .icon .add_menu_item (label = _ ("Preferences" ), command = self .config )
74+ self .icon .add_menu_separator ()
75+ self .icon .add_menu_item (label = _ ("Check for updates" ),
7576 command = lambda : UpdateChecker (self , True ))
76- self .icon .menu . add_command (label = _ ("About" ),
77+ self .icon .add_menu_item (label = _ ("About" ),
7778 command = lambda : About (self ))
78- self .icon .menu .add_separator ()
79- self .icon .menu .add_command (label = _ ("Quit" ), command = self .quit )
80- self .icon .bind ('<Button-1>' , self .display )
79+ self .icon .add_menu_separator ()
80+ self .icon .add_menu_item (label = _ ("Quit" ), command = self .quit )
81+ self .icon .loop (self )
82+ self .icon .bind_left_click (self .display )
8183
8284 self .style = Style (self )
8385 self .style .theme_use ('clam' )
86+ bg = self .cget ("background" )
87+ self .style .configure ("TLabel" , background = bg )
88+ self .style .configure ("TFrame" , background = bg )
89+ self .style .configure ("TButton" , background = bg )
90+ self .style .configure ("TCheckbutton" , background = bg )
91+ self .style .configure ("TMenubutton" , background = bg )
8492 self .style .map ('TCheckbutton' ,
8593 indicatorbackground = [('pressed' , '#dcdad5' ),
8694 ('!disabled' , 'alternate' , 'white' ),
@@ -118,37 +126,43 @@ def __init__(self):
118126 if CONFIG .getboolean ("General" , "check_update" ):
119127 UpdateChecker (self )
120128
129+ # replace Ctrl+A binding by select all for all entries
130+ self .bind_class ("TEntry" , "<Control-a>" , self .select_all_entry )
131+
132+ def select_all_entry (self , event ):
133+ event .widget .selection_range (0 , "end" )
134+
121135 def report_callback_exception (self , * args ):
122136 """Log exceptions."""
123137 err = "" .join (traceback .format_exception (* args ))
124138 logging .error (err )
125139
126140 def start_stop (self ):
127141 """Suspend checks."""
128- if self .icon .menu . entrycget ( 2 , "label" ) == _ ("Suspend" ):
129- self .icon . after_cancel (self .check_id )
130- self .icon . after_cancel (self .timer_id )
131- self .icon . after_cancel (self .notif_id )
132- self .icon . after_cancel (self .internet_id )
133- self .img . configure ( file = IMAGE )
134- self .icon .menu . entryconfigure ( 2 , label = _ ("Restart" ))
135- self .icon .menu . entryconfigure ( 0 , state = "disabled" )
136- self .icon .menu . entryconfigure ( 1 , state = "disabled" )
142+ if self .icon .get_item_label ( 3 ) == _ ("Suspend" ):
143+ self .after_cancel (self .check_id )
144+ self .after_cancel (self .timer_id )
145+ self .after_cancel (self .notif_id )
146+ self .after_cancel (self .internet_id )
147+ self .icon . change_icon ( IMAGE , "checkmails suspended" )
148+ self .icon .set_item_label ( 3 , _ ("Restart" ))
149+ self .icon .disable_item ( 1 )
150+ self .icon .disable_item ( 2 )
137151 else :
138- self .icon .menu . entryconfigure ( 2 , label = _ ("Suspend" ))
139- self .icon .menu . entryconfigure ( 0 , state = "normal" )
140- self .icon .menu . entryconfigure ( 1 , state = "normal" )
152+ self .icon .set_item_label ( 3 , _ ("Suspend" ))
153+ self .icon .enable_item ( 1 )
154+ self .icon .enable_item ( 2 )
141155 self .reconnect ()
142156
143157 def reconnect (self ):
144- self .icon . after_cancel (self .check_id )
158+ self .after_cancel (self .check_id )
145159 self .nb_unread = {box : 0 for box in self .info_conn }
146160 for box in self .boxes :
147161 self .logout (box , True , True )
148- self .check_id = self .icon . after (20000 , self .launch_check , False )
162+ self .check_id = self .after (20000 , self .launch_check , False )
149163
150- def display (self , event ):
151- if self .icon .menu . entrycget ( 2 , "label" ) == _ ("Suspend" ):
164+ def display (self ):
165+ if self .icon .get_item_label ( 3 ) == _ ("Suspend" ):
152166 notif = self .notif
153167 if not notif :
154168 notif = _ ("Checking..." )
@@ -165,9 +179,9 @@ def reset_conn(self):
165179 self .threads_connect = {}
166180 self .threads_reconnect = {}
167181 self .threads_check = {}
168- self .icon . after_cancel (self .timer_id )
169- self .icon . after_cancel (self .check_id )
170- self .icon . after_cancel (self .notif_id )
182+ self .after_cancel (self .timer_id )
183+ self .after_cancel (self .check_id )
184+ self .after_cancel (self .notif_id )
171185 self .get_info_conn ()
172186
173187 def get_info_conn (self ):
@@ -192,36 +206,37 @@ def get_info_conn(self):
192206 if not self .info_conn :
193207 self .notif = _ ("No active mailbox" )
194208 run (["notify-send" , "-i" , IMAGE2 , _ ("No active mailbox" ), _ ("Use the mailbox manager to configure a mailbox." )])
195- elif self .icon .menu . entrycget ( 2 , "label" ) == _ ("Suspend" ):
209+ elif self .icon .get_item_label ( 3 ) == _ ("Suspend" ):
196210 self .notif = ""
197211 for box in self .info_conn :
198212 self .connect (box )
199- self .icon . after_cancel (self .check_id )
213+ self .after_cancel (self .check_id )
200214 self .check_id = self .after (20000 , self .launch_check , False )
201215
202216 def change_icon (self , nbmail ):
203217 """Display the number of unread mails nbmail in the system tray icon."""
204218 nb = "%i" % nbmail
205219 im = Image .open (IMAGE )
206- draw = ImageDraw .Draw (im )
207- font_path = CONFIG .get ("General" , "font" )
208220 W , H = im .size
221+ draw = ImageDraw .Draw (im )
222+ font_path = TTF_FONTS [CONFIG .get ("General" , "font" )]
209223 try :
210- font = ImageFont .truetype (font_path , 10 )
224+ font = ImageFont .truetype (font_path , FONTSIZE )
211225 w , h = draw .textsize (nb , font = font )
212- draw .text (((W - w ) / 2 , (H - h ) / 2 ), nb , fill = (255 , 0 , 0 ), font = font )
226+ draw .text (((W - w ) / 2 , (H - h ) / 2 ), nb , fill = (255 , 0 , 0 ),
227+ font = font )
213228 except OSError :
214229 w , h = draw .textsize (nb )
215230 draw .text (((W - w ) / 2 , (H - h ) / 2 ), nb , fill = (255 , 0 , 0 ))
216231 im .save (ICON )
217- self .img . configure ( file = ICON )
232+ self .icon . change_icon ( ICON , "checkmails %s" % nb )
218233
219234 def config (self ):
220235 """Open config dialog to set times and language."""
221236 Config (self )
222237 self .time = CONFIG .getint ("General" , "time" )
223238 self .timeout = CONFIG .getint ("General" , "timeout" )
224- if self .icon .menu . entrycget ( 2 , "label" ) == _ ("Suspend" ):
239+ if self .icon .get_item_label ( 3 ) == _ ("Suspend" ):
225240 self .check_mails (False )
226241
227242 def manage_mailboxes (self ):
@@ -299,10 +314,10 @@ def connect_mailbox(self, box):
299314 run (["notify-send" , "-i" , "dialog-error" , _ ("Error" ),
300315 _ ("No internet connection." )])
301316 # cancel everything
302- self .icon . after_cancel (self .check_id )
303- self .icon . after_cancel (self .timer_id )
304- self .icon . after_cancel (self .notif_id )
305- self .icon . after_cancel (self .internet_id )
317+ self .after_cancel (self .check_id )
318+ self .after_cancel (self .timer_id )
319+ self .after_cancel (self .notif_id )
320+ self .after_cancel (self .internet_id )
306321 # periodically checks if the internet connection is turned on
307322 self .internet_id = self .after (self .timeout , self .test_connection )
308323 else :
@@ -324,7 +339,7 @@ def test_connection(self):
324339 if internet_on ():
325340 self .reset_conn ()
326341 else :
327- self .internet_id = self .icon . after (self .timeout , self .test_connection )
342+ self .internet_id = self .after (self .timeout , self .test_connection )
328343
329344 def logout_mailbox (self , box , reconnect ):
330345 """
@@ -373,8 +388,8 @@ def launch_check(self, force_notify=False):
373388 b = [self .threads_connect [box ].isAlive () for box in self .threads_connect ]
374389 if len (b ) < len (self .info_conn ) or True in b :
375390 logging .info ("Waiting for connexion ..." )
376- self .icon . after_cancel (self .check_id )
377- self .check_id = self .icon . after (20000 , self .launch_check , force_notify )
391+ self .after_cancel (self .check_id )
392+ self .check_id = self .after (20000 , self .launch_check , force_notify )
378393 else :
379394 logging .info ("Launching check" )
380395 self .check_mails (force_notify )
@@ -417,17 +432,17 @@ def check_mails(self, force_notify=True):
417432 display a notification even if there is no unread mail.
418433 """
419434 self .notif = _ ("Checking..." ) + "\n "
420- self .icon . after_cancel (self .timer_id )
421- self .icon . after_cancel (self .notif_id )
435+ self .after_cancel (self .timer_id )
436+ self .after_cancel (self .notif_id )
422437 for box , mail in self .boxes .items ():
423438 if not self .threads_connect [box ].isAlive () and (box not in self .threads_check or not self .threads_check [box ].isAlive ()):
424439 self .threads_check [box ] = Thread (target = self .check_mailbox ,
425440 name = 'check_' + box ,
426441 daemon = True ,
427442 args = (box ,))
428443 self .threads_check [box ].start ()
429- self .notif_id = self .icon . after (20000 , self .notify_unread_mails , force_notify )
430- self .timer_id = self .icon . after (self .time , self .check_mails , False )
444+ self .notif_id = self .after (20000 , self .notify_unread_mails , force_notify )
445+ self .timer_id = self .after (self .time , self .check_mails , False )
431446
432447 def notify_unread_mails (self , force_notify = True ):
433448 """
@@ -438,11 +453,14 @@ def notify_unread_mails(self, force_notify=True):
438453 """
439454 b = [self .threads_check [box ].isAlive () for box in self .threads_check ]
440455 if len (b ) < len (self .info_conn ) or True in b :
441- self .notif_id = self .icon . after (20000 , self .notify_unread_mails , force_notify )
456+ self .notif_id = self .after (20000 , self .notify_unread_mails , force_notify )
442457 else :
443458 if self .notif != _ ("Checking..." ) + "\n " :
444- self .notif = self .notif [:- 2 ].split ("\n " )[1 ]
445- run (["notify-send" , "-i" , IMAGE2 , _ ("Unread mails" ), self .notif ])
459+ try :
460+ self .notif = self .notif [:- 2 ].split ("\n " )[1 ]
461+ run (["notify-send" , "-i" , IMAGE2 , _ ("Unread mails" ), self .notif ])
462+ except IndexError :
463+ run (["notify-send" , "-i" , IMAGE2 , _ ("Unread mails" ), self .notif ])
446464 elif force_notify :
447465 run (["notify-send" , "-i" , IMAGE2 , _ ("Unread mails" ), _ ("No unread mail" )])
448466 self .notif = _ ("No unread mail" )
@@ -458,6 +476,7 @@ def quit(self):
458476 for box in self .info_conn :
459477 self .logout (box )
460478 try :
479+ self .after_cancel (self .loop_id )
461480 self .destroy ()
462481 except TclError :
463482 # depending on the pending processes when the app is destroyed
0 commit comments