use webbrowser module to open uri instead of using popen. Fixes #5751
[gajim.git] / src / config.py
blobd2807ae748cbc6104bec32d2479af10cdaa0a341
1 # -*- coding:utf-8 -*-
2 ## src/config.py
3 ##
4 ## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
5 ## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
6 ## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
7 ## Stéphan Kochen <stephan AT kochen.nl>
8 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
9 ## Nikos Kouremenos <kourem AT gmail.com>
10 ## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
11 ## Copyright (C) 2006-2007 Travis Shirk <travis AT pobox.com>
12 ## Stefan Bethge <stefan AT lanpartei.de>
13 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
14 ## Copyright (C) 2007 James Newton <redshodan AT gmail.com>
15 ## Julien Pivotto <roidelapluie AT gmail.com>
16 ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
17 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
19 ## This file is part of Gajim.
21 ## Gajim is free software; you can redistribute it and/or modify
22 ## it under the terms of the GNU General Public License as published
23 ## by the Free Software Foundation; version 3 only.
25 ## Gajim is distributed in the hope that it will be useful,
26 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
27 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 ## GNU General Public License for more details.
30 ## You should have received a copy of the GNU General Public License
31 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
34 import gtk
35 import gobject
36 import os, sys
37 import common.config
38 import common.sleepy
39 from common.i18n import Q_
41 import gtkgui_helpers
42 import dialogs
43 import cell_renderer_image
44 import message_control
45 import chat_control
46 import dataforms_widget
48 try:
49 import gtkspell
50 HAS_GTK_SPELL = True
51 except ImportError:
52 HAS_GTK_SPELL = False
54 from common import helpers
55 from common import gajim
56 from common import connection
57 from common import passwords
58 from common.zeroconf import connection_zeroconf
59 from common import dataforms
60 from common import gpg
61 from common import ged
63 try:
64 from common.multimedia_helpers import AudioInputManager, AudioOutputManager
65 from common.multimedia_helpers import VideoInputManager, VideoOutputManager
66 HAS_GST = True
67 except ImportError:
68 HAS_GST = False
70 from common.exceptions import GajimGeneralException
72 #---------- PreferencesWindow class -------------#
73 class PreferencesWindow:
74 """
75 Class for Preferences window
76 """
78 def on_preferences_window_destroy(self, widget):
79 """
80 Close window
81 """
82 del gajim.interface.instances['preferences']
84 def on_close_button_clicked(self, widget):
85 self.window.destroy()
87 def __init__(self):
88 """
89 Initialize Preferences window
90 """
91 self.xml = gtkgui_helpers.get_gtk_builder('preferences_window.ui')
92 self.window = self.xml.get_object('preferences_window')
93 self.window.set_transient_for(gajim.interface.roster.window)
94 self.notebook = self.xml.get_object('preferences_notebook')
95 self.one_window_type_combobox =\
96 self.xml.get_object('one_window_type_combobox')
97 self.iconset_combobox = self.xml.get_object('iconset_combobox')
98 self.notify_on_signin_checkbutton = self.xml.get_object(
99 'notify_on_signin_checkbutton')
100 self.notify_on_signout_checkbutton = self.xml.get_object(
101 'notify_on_signout_checkbutton')
102 self.auto_popup_away_checkbutton = self.xml.get_object(
103 'auto_popup_away_checkbutton')
104 self.sound_dnd_checkbutton = self.xml.get_object('sound_dnd_checkbutton')
105 self.auto_away_checkbutton = self.xml.get_object('auto_away_checkbutton')
106 self.auto_away_time_spinbutton = self.xml.get_object(
107 'auto_away_time_spinbutton')
108 self.auto_away_message_entry = self.xml.get_object(
109 'auto_away_message_entry')
110 self.auto_xa_checkbutton = self.xml.get_object('auto_xa_checkbutton')
111 self.auto_xa_time_spinbutton = self.xml.get_object(
112 'auto_xa_time_spinbutton')
113 self.auto_xa_message_entry = self.xml.get_object('auto_xa_message_entry')
115 ### General tab ###
116 # Display avatars in roster
117 st = gajim.config.get('show_avatars_in_roster')
118 self.xml.get_object('show_avatars_in_roster_checkbutton'). \
119 set_active(st)
121 # Display status msg under contact name in roster
122 st = gajim.config.get('show_status_msgs_in_roster')
123 self.xml.get_object('show_status_msgs_in_roster_checkbutton'). \
124 set_active( st)
126 # Display mood in roster
127 st = gajim.config.get('show_mood_in_roster')
128 self.xml.get_object('show_mood_in_roster_checkbutton'). \
129 set_active(st)
131 # Display activity in roster
132 st = gajim.config.get('show_activity_in_roster')
133 self.xml.get_object('show_activity_in_roster_checkbutton'). \
134 set_active(st)
136 # Display tunes in roster
137 st = gajim.config.get('show_tunes_in_roster')
138 self.xml.get_object('show_tunes_in_roster_checkbutton'). \
139 set_active(st)
141 # Display location in roster
142 st = gajim.config.get('show_location_in_roster')
143 self.xml.get_object('show_location_in_roster_checkbutton'). \
144 set_active(st)
146 # Sort contacts by show
147 st = gajim.config.get('sort_by_show_in_roster')
148 self.xml.get_object('sort_by_show_in_roster_checkbutton').set_active(st)
149 st = gajim.config.get('sort_by_show_in_muc')
150 self.xml.get_object('sort_by_show_in_muc_checkbutton').set_active(st)
152 # emoticons
153 emoticons_combobox = self.xml.get_object('emoticons_combobox')
154 emoticons_list = os.listdir(os.path.join(gajim.DATA_DIR, 'emoticons'))
155 # user themes
156 if os.path.isdir(gajim.MY_EMOTS_PATH):
157 emoticons_list += os.listdir(gajim.MY_EMOTS_PATH)
158 renderer_text = gtk.CellRendererText()
159 emoticons_combobox.pack_start(renderer_text, True)
160 emoticons_combobox.add_attribute(renderer_text, 'text', 0)
161 model = gtk.ListStore(str)
162 emoticons_combobox.set_model(model)
163 l = []
164 for dir_ in emoticons_list:
165 if not os.path.isdir(os.path.join(gajim.DATA_DIR, 'emoticons', dir_)) \
166 and not os.path.isdir(os.path.join(gajim.MY_EMOTS_PATH, dir_)) :
167 continue
168 if dir_ != '.svn':
169 l.append(dir_)
170 l.append(_('Disabled'))
171 for i in xrange(len(l)):
172 model.append([l[i]])
173 if gajim.config.get('emoticons_theme') == l[i]:
174 emoticons_combobox.set_active(i)
175 if not gajim.config.get('emoticons_theme'):
176 emoticons_combobox.set_active(len(l)-1)
178 # Set default for single window type
179 choices = common.config.opt_one_window_types
180 type_ = gajim.config.get('one_message_window')
181 if type_ in choices:
182 self.one_window_type_combobox.set_active(choices.index(type_))
183 else:
184 self.one_window_type_combobox.set_active(0)
186 # Show roster on startup
187 show_roster_combobox = self.xml.get_object('show_roster_on_startup')
188 choices = common.config.opt_show_roster_on_startup
189 type_ = gajim.config.get('show_roster_on_startup')
190 if type_ in choices:
191 show_roster_combobox.set_active(choices.index(type_))
192 else:
193 show_roster_combobox.set_active(0)
195 # Compact View
196 st = gajim.config.get('compact_view')
197 self.xml.get_object('compact_view_checkbutton').set_active(st)
199 # Ignore XHTML
200 st = gajim.config.get('ignore_incoming_xhtml')
201 self.xml.get_object('xhtml_checkbutton').set_active(st)
203 # use speller
204 if HAS_GTK_SPELL:
205 st = gajim.config.get('use_speller')
206 self.xml.get_object('speller_checkbutton').set_active(st)
207 else:
208 self.xml.get_object('speller_checkbutton').set_sensitive(False)
210 ### Style tab ###
211 # Themes
212 theme_combobox = self.xml.get_object('theme_combobox')
213 cell = gtk.CellRendererText()
214 theme_combobox.pack_start(cell, True)
215 theme_combobox.add_attribute(cell, 'text', 0)
216 self.update_theme_list()
218 # iconset
219 iconsets_list = os.listdir(os.path.join(gajim.DATA_DIR, 'iconsets'))
220 if os.path.isdir(gajim.MY_ICONSETS_PATH):
221 iconsets_list += os.listdir(gajim.MY_ICONSETS_PATH)
222 # new model, image in 0, string in 1
223 model = gtk.ListStore(gtk.Image, str)
224 renderer_image = cell_renderer_image.CellRendererImage(0, 0)
225 renderer_text = gtk.CellRendererText()
226 renderer_text.set_property('xpad', 5)
227 self.iconset_combobox.pack_start(renderer_image, expand = False)
228 self.iconset_combobox.pack_start(renderer_text, expand = True)
229 self.iconset_combobox.set_attributes(renderer_text, text = 1)
230 self.iconset_combobox.add_attribute(renderer_image, 'image', 0)
231 self.iconset_combobox.set_model(model)
232 l = []
233 for dir in iconsets_list:
234 if not os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', dir)) \
235 and not os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, dir)):
236 continue
237 if dir != '.svn' and dir != 'transports':
238 l.append(dir)
239 if l.count == 0:
240 l.append(' ')
241 for i in xrange(len(l)):
242 preview = gtk.Image()
243 files = []
244 files.append(os.path.join(helpers.get_iconset_path(l[i]), '16x16',
245 'online.png'))
246 files.append(os.path.join(helpers.get_iconset_path(l[i]), '16x16',
247 'online.gif'))
248 for file_ in files:
249 if os.path.exists(file_):
250 preview.set_from_file(file_)
251 model.append([preview, l[i]])
252 if gajim.config.get('iconset') == l[i]:
253 self.iconset_combobox.set_active(i)
255 # Use transports iconsets
256 st = gajim.config.get('use_transports_iconsets')
257 self.xml.get_object('transports_iconsets_checkbutton').set_active(st)
259 # Color widgets
260 self.draw_color_widgets()
262 # Font for messages
263 font = gajim.config.get('conversation_font')
264 # try to set default font for the current desktop env
265 fontbutton = self.xml.get_object('conversation_fontbutton')
266 if font == '':
267 fontbutton.set_sensitive(False)
268 self.xml.get_object('default_chat_font').set_active(True)
269 else:
270 fontbutton.set_font_name(font)
272 ### Personal Events tab ###
273 # outgoing send chat state notifications
274 st = gajim.config.get('outgoing_chat_state_notifications')
275 combo = self.xml.get_object('outgoing_chat_states_combobox')
276 if st == 'all':
277 combo.set_active(0)
278 elif st == 'composing_only':
279 combo.set_active(1)
280 else: # disabled
281 combo.set_active(2)
283 # displayed send chat state notifications
284 st = gajim.config.get('displayed_chat_state_notifications')
285 combo = self.xml.get_object('displayed_chat_states_combobox')
286 if st == 'all':
287 combo.set_active(0)
288 elif st == 'composing_only':
289 combo.set_active(1)
290 else: # disabled
291 combo.set_active(2)
294 ### Notifications tab ###
295 # On new event
296 on_event_combobox = self.xml.get_object('on_event_combobox')
297 if gajim.config.get('autopopup'):
298 on_event_combobox.set_active(0)
299 elif gajim.config.get('notify_on_new_message'):
300 on_event_combobox.set_active(1)
301 else:
302 on_event_combobox.set_active(2)
304 # notify on online statuses
305 st = gajim.config.get('notify_on_signin')
306 self.notify_on_signin_checkbutton.set_active(st)
308 # notify on offline statuses
309 st = gajim.config.get('notify_on_signout')
310 self.notify_on_signout_checkbutton.set_active(st)
312 # autopopupaway
313 st = gajim.config.get('autopopupaway')
314 self.auto_popup_away_checkbutton.set_active(st)
316 # sounddnd
317 st = gajim.config.get('sounddnd')
318 self.sound_dnd_checkbutton.set_active(st)
320 # Systray
321 systray_combobox = self.xml.get_object('systray_combobox')
322 if gajim.config.get('trayicon') == 'never':
323 systray_combobox.set_active(0)
324 elif gajim.config.get('trayicon') == 'on_event':
325 systray_combobox.set_active(1)
326 else:
327 systray_combobox.set_active(2)
329 # sounds
330 if gajim.config.get('sounds_on'):
331 self.xml.get_object('play_sounds_checkbutton').set_active(True)
332 else:
333 self.xml.get_object('manage_sounds_button').set_sensitive(False)
335 # Notify user of new gmail e-mail messages,
336 # make checkbox sensitive if user has a gtalk account
337 frame_gmail = self.xml.get_object('frame_gmail')
338 notify_gmail_checkbutton = self.xml.get_object('notify_gmail_checkbutton')
339 notify_gmail_extra_checkbutton = self.xml.get_object(
340 'notify_gmail_extra_checkbutton')
342 for account in gajim.config.get_per('accounts'):
343 jid = gajim.get_jid_from_account(account)
344 if gajim.get_server_from_jid(jid) in gajim.gmail_domains:
345 frame_gmail.set_sensitive(True)
346 st = gajim.config.get('notify_on_new_gmail_email')
347 notify_gmail_checkbutton.set_active(st)
348 st = gajim.config.get('notify_on_new_gmail_email_extra')
349 notify_gmail_extra_checkbutton.set_active(st)
350 break
352 #### Status tab ###
353 # Autoaway
354 st = gajim.config.get('autoaway')
355 self.auto_away_checkbutton.set_active(st)
357 # Autoawaytime
358 st = gajim.config.get('autoawaytime')
359 self.auto_away_time_spinbutton.set_value(st)
360 self.auto_away_time_spinbutton.set_sensitive(gajim.config.get('autoaway'))
362 # autoaway message
363 st = gajim.config.get('autoaway_message')
364 self.auto_away_message_entry.set_text(st)
365 self.auto_away_message_entry.set_sensitive(gajim.config.get('autoaway'))
367 # Autoxa
368 st = gajim.config.get('autoxa')
369 self.auto_xa_checkbutton.set_active(st)
371 # Autoxatime
372 st = gajim.config.get('autoxatime')
373 self.auto_xa_time_spinbutton.set_value(st)
374 self.auto_xa_time_spinbutton.set_sensitive(gajim.config.get('autoxa'))
376 # autoxa message
377 st = gajim.config.get('autoxa_message')
378 self.auto_xa_message_entry.set_text(st)
379 self.auto_xa_message_entry.set_sensitive(gajim.config.get('autoxa'))
381 from common import sleepy
382 if not sleepy.SUPPORTED:
383 self.xml.get_object('autoaway_table').set_sensitive(False)
385 # ask_status when online / offline
386 st = gajim.config.get('ask_online_status')
387 self.xml.get_object('prompt_online_status_message_checkbutton').\
388 set_active(st)
389 st = gajim.config.get('ask_offline_status')
390 self.xml.get_object('prompt_offline_status_message_checkbutton').\
391 set_active(st)
393 # Default Status messages
394 self.default_msg_tree = self.xml.get_object('default_msg_treeview')
395 col2 = self.default_msg_tree.rc_get_style().bg[gtk.STATE_ACTIVE].\
396 to_string()
397 # (status, translated_status, message, enabled)
398 model = gtk.ListStore(str, str, str, bool)
399 self.default_msg_tree.set_model(model)
400 col = gtk.TreeViewColumn(_('Status'))
401 col.set_resizable(True)
402 self.default_msg_tree.append_column(col)
403 renderer = gtk.CellRendererText()
404 col.pack_start(renderer, False)
405 col.set_attributes(renderer, text = 1)
406 col = gtk.TreeViewColumn(_('Default Message'))
407 col.set_resizable(True)
408 self.default_msg_tree.append_column(col)
409 renderer = gtk.CellRendererText()
410 col.pack_start(renderer, True)
411 col.set_attributes(renderer, text = 2)
412 renderer.connect('edited', self.on_default_msg_cell_edited)
413 renderer.set_property('editable', True)
414 renderer.set_property('cell-background', col2)
415 col = gtk.TreeViewColumn(_('Enabled'))
416 col.set_resizable(True)
417 self.default_msg_tree.append_column(col)
418 renderer = gtk.CellRendererToggle()
419 col.pack_start(renderer, False)
420 col.set_attributes(renderer, active = 3)
421 renderer.set_property('activatable', True)
422 renderer.connect('toggled', self.default_msg_toggled_cb)
423 self.fill_default_msg_treeview()
425 # Status messages
426 self.msg_tree = self.xml.get_object('msg_treeview')
427 model = gtk.ListStore(str, str, str, str, str, str, str)
428 self.msg_tree.set_model(model)
429 col = gtk.TreeViewColumn('name')
430 self.msg_tree.append_column(col)
431 renderer = gtk.CellRendererText()
432 col.pack_start(renderer, True)
433 col.set_attributes(renderer, text = 0)
434 renderer.connect('edited', self.on_msg_cell_edited)
435 renderer.set_property('editable', True)
436 self.fill_msg_treeview()
437 buf = self.xml.get_object('msg_textview').get_buffer()
438 buf.connect('changed', self.on_msg_textview_changed)
440 ### Audio / Video tab ###
441 def create_av_combobox(opt_name, device_dict, config_name=None,
442 key=None):
443 combobox = self.xml.get_object(opt_name + '_combobox')
444 cell = gtk.CellRendererText()
445 combobox.pack_start(cell, True)
446 combobox.add_attribute(cell, 'text', 0)
447 model = gtk.ListStore(str, str)
448 combobox.set_model(model)
449 if config_name:
450 config = gajim.config.get(config_name)
451 else:
452 config = gajim.config.get(opt_name + '_device')
454 for index, (name, value) in enumerate(sorted(device_dict.\
455 iteritems(), key=key)):
456 model.append((name, value))
457 if config == value:
458 combobox.set_active(index)
460 if HAS_GST:
461 create_av_combobox('audio_input', AudioInputManager().get_devices())
462 create_av_combobox('audio_output', AudioOutputManager().get_devices(
464 create_av_combobox('video_input', VideoInputManager().get_devices())
465 create_av_combobox('video_output', VideoOutputManager().get_devices(
468 create_av_combobox('video_framerate', {_('Default'): '',
469 '15fps': '15/1', '10fps': '10/1', '5fps': '5/1',
470 '2.5fps': '5/2'}, 'video_framerate', key=lambda x: -1 if \
471 not x[1] else float(x[0][:-3]))
472 create_av_combobox('video_size', {_('Default'): '',
473 '800x600': '800x600', '640x480': '640x480',
474 '320x240': '320x240'}, 'video_size', key=lambda x: -1 if \
475 not x[1] else int(x[0][:3]))
477 else:
478 for opt_name in ('audio_input', 'audio_output', 'video_input',
479 'video_output', 'video_framerate', 'video_size'):
480 combobox = self.xml.get_object(opt_name + '_combobox')
481 combobox.set_sensitive(False)
483 # STUN
484 cb = self.xml.get_object('stun_checkbutton')
485 st = gajim.config.get('use_stun_server')
486 cb.set_active(st)
488 entry = self.xml.get_object('stun_server_entry')
489 entry.set_text(gajim.config.get('stun_server'))
490 if not st:
491 entry.set_sensitive(False)
493 ### Advanced tab ###
494 # open links with
495 if os.name == 'nt':
496 applications_frame = self.xml.get_object('applications_frame')
497 applications_frame.set_no_show_all(True)
498 applications_frame.hide()
499 else:
500 self.applications_combobox = self.xml.get_object(
501 'applications_combobox')
502 self.xml.get_object('custom_apps_frame').hide()
503 self.xml.get_object('custom_apps_frame').set_no_show_all(True)
505 if gajim.config.get('autodetect_browser_mailer'):
506 self.applications_combobox.set_active(0)
507 else:
508 self.applications_combobox.set_active(1)
509 self.xml.get_object('custom_apps_frame').show()
511 self.xml.get_object('custom_browser_entry').set_text(
512 gajim.config.get('custombrowser'))
513 self.xml.get_object('custom_mail_client_entry').set_text(
514 gajim.config.get('custommailapp'))
515 self.xml.get_object('custom_file_manager_entry').set_text(
516 gajim.config.get('custom_file_manager'))
518 # log status changes of contacts
519 st = gajim.config.get('log_contact_status_changes')
520 self.xml.get_object('log_show_changes_checkbutton').set_active(st)
522 # log encrypted chat sessions
523 w = self.xml.get_object('log_encrypted_chats_checkbutton')
524 st = self.get_per_account_option('log_encrypted_sessions')
525 if st == 'mixed':
526 w.set_inconsistent(True)
527 else:
528 w.set_active(st)
530 # send os info
531 w = self.xml.get_object('send_os_info_checkbutton')
532 st = self.get_per_account_option('send_os_info')
533 if st == 'mixed':
534 w.set_inconsistent(True)
535 else:
536 w.set_active(st)
538 # send idle time
539 w = self.xml.get_object('send_idle_time_checkbutton')
540 st = self.get_per_account_option('send_idle_time')
541 if st == 'mixed':
542 w.set_inconsistent(True)
543 else:
544 w.set_active(st)
546 # check if gajm is default
547 st = gajim.config.get('check_if_gajim_is_default')
548 self.xml.get_object('check_default_client_checkbutton').set_active(st)
550 # Ignore messages from unknown contacts
551 w = self.xml.get_object('ignore_events_from_unknown_contacts_checkbutton')
552 st = self.get_per_account_option('ignore_unknown_contacts')
553 if st == 'mixed':
554 w.set_inconsistent(True)
555 else:
556 w.set_active(st)
558 self.xml.connect_signals(self)
560 self.msg_tree.get_model().connect('row-changed',
561 self.on_msg_treemodel_row_changed)
562 self.msg_tree.get_model().connect('row-deleted',
563 self.on_msg_treemodel_row_deleted)
564 self.default_msg_tree.get_model().connect('row-changed',
565 self.on_default_msg_treemodel_row_changed)
567 self.theme_preferences = None
568 self.sounds_preferences = None
570 self.notebook.set_current_page(0)
572 self.window.show_all()
573 gtkgui_helpers.possibly_move_window_in_current_desktop(self.window)
575 def on_preferences_window_key_press_event(self, widget, event):
576 if event.keyval == gtk.keysyms.Escape:
577 self.window.hide()
579 def get_per_account_option(self, opt):
581 Return the value of the option opt if it's the same in all accounts else
582 returns "mixed"
584 if len(gajim.connections) == 0:
585 # a non existant key return default value
586 return gajim.config.get_per('accounts', '__default__', opt)
587 val = None
588 for account in gajim.connections:
589 v = gajim.config.get_per('accounts', account, opt)
590 if val is None:
591 val = v
592 elif val != v:
593 return 'mixed'
594 return val
596 def on_checkbutton_toggled(self, widget, config_name,
597 change_sensitivity_widgets=None):
598 gajim.config.set(config_name, widget.get_active())
599 if change_sensitivity_widgets:
600 for w in change_sensitivity_widgets:
601 w.set_sensitive(widget.get_active())
602 gajim.interface.save_config()
604 def on_per_account_checkbutton_toggled(self, widget, config_name,
605 change_sensitivity_widgets=None):
606 for account in gajim.connections:
607 gajim.config.set_per('accounts', account, config_name,
608 widget.get_active())
609 if change_sensitivity_widgets:
610 for w in change_sensitivity_widgets:
611 w.set_sensitive(widget.get_active())
612 gajim.interface.save_config()
614 def _get_all_controls(self):
615 for ctrl in gajim.interface.msg_win_mgr.get_controls():
616 yield ctrl
617 for account in gajim.connections:
618 for ctrl in gajim.interface.minimized_controls[account].values():
619 yield ctrl
621 def _get_all_muc_controls(self):
622 for ctrl in gajim.interface.msg_win_mgr.get_controls(
623 message_control.TYPE_GC):
624 yield ctrl
625 for account in gajim.connections:
626 for ctrl in gajim.interface.minimized_controls[account].values():
627 yield ctrl
629 def on_sort_by_show_in_roster_checkbutton_toggled(self, widget):
630 self.on_checkbutton_toggled(widget, 'sort_by_show_in_roster')
631 gajim.interface.roster.setup_and_draw_roster()
633 def on_sort_by_show_in_muc_checkbutton_toggled(self, widget):
634 self.on_checkbutton_toggled(widget, 'sort_by_show_in_muc')
635 # Redraw groupchats
636 for ctrl in self._get_all_muc_controls():
637 ctrl.draw_roster()
639 def on_show_avatars_in_roster_checkbutton_toggled(self, widget):
640 self.on_checkbutton_toggled(widget, 'show_avatars_in_roster')
641 gajim.interface.roster.setup_and_draw_roster()
642 # Redraw groupchats (in an ugly way)
643 for ctrl in self._get_all_muc_controls():
644 ctrl.draw_roster()
646 def on_show_status_msgs_in_roster_checkbutton_toggled(self, widget):
647 self.on_checkbutton_toggled(widget, 'show_status_msgs_in_roster')
648 gajim.interface.roster.setup_and_draw_roster()
649 for ctrl in self._get_all_muc_controls():
650 ctrl.update_ui()
652 def on_show_mood_in_roster_checkbutton_toggled(self, widget):
653 self.on_checkbutton_toggled(widget, 'show_mood_in_roster')
654 gajim.interface.roster.setup_and_draw_roster()
656 def on_show_activity_in_roster_checkbutton_toggled(self, widget):
657 self.on_checkbutton_toggled(widget, 'show_activity_in_roster')
658 gajim.interface.roster.setup_and_draw_roster()
660 def on_show_tunes_in_roster_checkbutton_toggled(self, widget):
661 self.on_checkbutton_toggled(widget, 'show_tunes_in_roster')
662 gajim.interface.roster.setup_and_draw_roster()
664 def on_show_location_in_roster_checkbutton_toggled(self, widget):
665 self.on_checkbutton_toggled(widget, 'show_location_in_roster')
666 gajim.interface.roster.setup_and_draw_roster()
668 def on_emoticons_combobox_changed(self, widget):
669 active = widget.get_active()
670 model = widget.get_model()
671 emot_theme = model[active][0].decode('utf-8')
672 if emot_theme == _('Disabled'):
673 gajim.config.set('emoticons_theme', '')
674 else:
675 gajim.config.set('emoticons_theme', emot_theme)
677 gajim.interface.init_emoticons(need_reload = True)
678 gajim.interface.make_regexps()
679 self.toggle_emoticons()
681 def toggle_emoticons(self):
683 Update emoticons state in Opened Chat Windows
685 for ctrl in self._get_all_controls():
686 ctrl.toggle_emoticons()
688 def on_one_window_type_combo_changed(self, widget):
689 active = widget.get_active()
690 config_type = common.config.opt_one_window_types[active]
691 gajim.config.set('one_message_window', config_type)
692 gajim.interface.save_config()
693 gajim.interface.msg_win_mgr.reconfig()
695 def on_show_roster_on_startup_changed(self, widget):
696 active = widget.get_active()
697 config_type = common.config.opt_show_roster_on_startup[active]
698 gajim.config.set('show_roster_on_startup', config_type)
699 gajim.interface.save_config()
701 def on_compact_view_checkbutton_toggled(self, widget):
702 active = widget.get_active()
703 for ctrl in self._get_all_controls():
704 ctrl.chat_buttons_set_visible(active)
705 gajim.config.set('compact_view', active)
706 gajim.interface.save_config()
708 def on_xhtml_checkbutton_toggled(self, widget):
709 self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
710 helpers.update_optional_features()
712 def apply_speller(self):
713 for ctrl in self._get_all_controls():
714 if isinstance(ctrl, chat_control.ChatControlBase):
715 try:
716 spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
717 except (TypeError, RuntimeError, OSError):
718 spell_obj = None
720 if not spell_obj:
721 ctrl.set_speller()
723 def remove_speller(self):
724 for ctrl in self._get_all_controls():
725 if isinstance(ctrl, chat_control.ChatControlBase):
726 try:
727 spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
728 except (TypeError, RuntimeError):
729 spell_obj = None
730 if spell_obj:
731 spell_obj.detach()
733 def on_speller_checkbutton_toggled(self, widget):
734 active = widget.get_active()
735 gajim.config.set('use_speller', active)
736 gajim.interface.save_config()
737 if active:
738 lang = gajim.config.get('speller_language')
739 if not lang:
740 lang = gajim.LANG
741 tv = gtk.TextView()
742 try:
743 gtkspell.Spell(tv, lang)
744 except (TypeError, RuntimeError, OSError):
745 dialogs.ErrorDialog(
746 _('Dictionary for lang %s not available') % lang,
747 _('You have to install %s dictionary to use spellchecking, or '
748 'choose another language by setting the speller_language option.'
749 ) % lang)
750 gajim.config.set('use_speller', False)
751 widget.set_active(False)
752 else:
753 gajim.config.set('speller_language', lang)
754 self.apply_speller()
755 else:
756 self.remove_speller()
758 def on_theme_combobox_changed(self, widget):
759 model = widget.get_model()
760 active = widget.get_active()
761 config_theme = model[active][0].decode('utf-8').replace(' ', '_')
763 gajim.config.set('roster_theme', config_theme)
765 # begin repainting themed widgets throughout
766 gajim.interface.roster.repaint_themed_widgets()
767 gajim.interface.roster.change_roster_style(None)
768 gajim.interface.save_config()
770 def update_theme_list(self):
771 theme_combobox = self.xml.get_object('theme_combobox')
772 model = gtk.ListStore(str)
773 theme_combobox.set_model(model)
774 i = 0
775 for config_theme in gajim.config.get_per('themes'):
776 theme = config_theme.replace('_', ' ')
777 model.append([theme])
778 if gajim.config.get('roster_theme') == config_theme:
779 theme_combobox.set_active(i)
780 i += 1
782 def on_manage_theme_button_clicked(self, widget):
783 if self.theme_preferences is None:
784 self.theme_preferences = dialogs.GajimThemesWindow()
785 else:
786 self.theme_preferences.window.present()
787 self.theme_preferences.select_active_theme()
789 def on_iconset_combobox_changed(self, widget):
790 model = widget.get_model()
791 active = widget.get_active()
792 icon_string = model[active][1].decode('utf-8')
793 gajim.config.set('iconset', icon_string)
794 gtkgui_helpers.reload_jabber_state_images()
795 gajim.interface.save_config()
797 def on_transports_iconsets_checkbutton_toggled(self, widget):
798 self.on_checkbutton_toggled(widget, 'use_transports_iconsets')
799 gtkgui_helpers.reload_jabber_state_images()
801 def on_outgoing_chat_states_combobox_changed(self, widget):
802 active = widget.get_active()
803 old_value = gajim.config.get('outgoing_chat_state_notifications')
804 if active == 0: # all
805 gajim.config.set('outgoing_chat_state_notifications', 'all')
806 elif active == 1: # only composing
807 gajim.config.set('outgoing_chat_state_notifications', 'composing_only')
808 else: # disabled
809 gajim.config.set('outgoing_chat_state_notifications', 'disabled')
810 new_value = gajim.config.get('outgoing_chat_state_notifications')
811 if 'disabled' in (old_value, new_value):
812 # we changed from disabled to sth else or vice versa
813 helpers.update_optional_features()
815 def on_displayed_chat_states_combobox_changed(self, widget):
816 active = widget.get_active()
817 if active == 0: # all
818 gajim.config.set('displayed_chat_state_notifications', 'all')
819 elif active == 1: # only composing
820 gajim.config.set('displayed_chat_state_notifications',
821 'composing_only')
822 else: # disabled
823 gajim.config.set('displayed_chat_state_notifications', 'disabled')
825 def on_ignore_events_from_unknown_contacts_checkbutton_toggled(self, widget):
826 widget.set_inconsistent(False)
827 self.on_per_account_checkbutton_toggled(widget, 'ignore_unknown_contacts')
829 def on_on_event_combobox_changed(self, widget):
830 active = widget.get_active()
831 if active == 0:
832 gajim.config.set('autopopup', True)
833 gajim.config.set('notify_on_new_message', False)
834 elif active == 1:
835 gajim.config.set('autopopup', False)
836 gajim.config.set('notify_on_new_message', True)
837 else:
838 gajim.config.set('autopopup', False)
839 gajim.config.set('notify_on_new_message', False)
841 def on_notify_on_signin_checkbutton_toggled(self, widget):
842 self.on_checkbutton_toggled(widget, 'notify_on_signin')
844 def on_notify_on_signout_checkbutton_toggled(self, widget):
845 self.on_checkbutton_toggled(widget, 'notify_on_signout')
847 def on_auto_popup_away_checkbutton_toggled(self, widget):
848 self.on_checkbutton_toggled(widget, 'autopopupaway')
850 def on_sound_dnd_checkbutton_toggled(self, widget):
851 self.on_checkbutton_toggled(widget, 'sounddnd')
853 def on_systray_combobox_changed(self, widget):
854 active = widget.get_active()
855 if active == 0:
856 gajim.config.set('trayicon', 'never')
857 gajim.interface.systray_enabled = False
858 gajim.interface.systray.hide_icon()
859 elif active == 1:
860 gajim.config.set('trayicon', 'on_event')
861 gajim.interface.systray_enabled = True
862 gajim.interface.systray.show_icon()
863 else:
864 gajim.config.set('trayicon', 'always')
865 gajim.interface.systray_enabled = True
866 gajim.interface.systray.show_icon()
868 def on_advanced_notifications_button_clicked(self, widget):
869 dialogs.AdvancedNotificationsWindow()
871 def on_play_sounds_checkbutton_toggled(self, widget):
872 self.on_checkbutton_toggled(widget, 'sounds_on',
873 [self.xml.get_object('manage_sounds_button')])
875 def on_manage_sounds_button_clicked(self, widget):
876 if self.sounds_preferences is None:
877 self.sounds_preferences = ManageSoundsWindow()
878 else:
879 self.sounds_preferences.window.present()
881 def update_text_tags(self):
883 Update color tags in opened chat windows
885 for ctrl in self._get_all_controls():
886 ctrl.update_tags()
888 def on_preference_widget_color_set(self, widget, text):
889 color = widget.get_color()
890 color_string = gtkgui_helpers.make_color_string(color)
891 gajim.config.set(text, color_string)
892 self.update_text_tags()
893 gajim.interface.save_config()
895 def on_preference_widget_font_set(self, widget, text):
896 if widget:
897 font = widget.get_font_name()
898 else:
899 font = ''
900 gajim.config.set(text, font)
901 self.update_text_font()
902 gajim.interface.save_config()
904 def update_text_font(self):
906 Update text font in opened chat windows
908 for ctrl in self._get_all_controls():
909 ctrl.update_font()
911 def on_incoming_nick_colorbutton_color_set(self, widget):
912 self.on_preference_widget_color_set(widget, 'inmsgcolor')
914 def on_outgoing_nick_colorbutton_color_set(self, widget):
915 self.on_preference_widget_color_set(widget, 'outmsgcolor')
917 def on_incoming_msg_colorbutton_color_set(self, widget):
918 self.on_preference_widget_color_set(widget, 'inmsgtxtcolor')
920 def on_outgoing_msg_colorbutton_color_set(self, widget):
921 self.on_preference_widget_color_set(widget, 'outmsgtxtcolor')
923 def on_url_msg_colorbutton_color_set(self, widget):
924 self.on_preference_widget_color_set(widget, 'urlmsgcolor')
926 def on_status_msg_colorbutton_color_set(self, widget):
927 self.on_preference_widget_color_set(widget, 'statusmsgcolor')
929 def on_conversation_fontbutton_font_set(self, widget):
930 self.on_preference_widget_font_set(widget, 'conversation_font')
932 def on_default_chat_font_toggled(self, widget):
933 font_widget = self.xml.get_object('conversation_fontbutton')
934 if widget.get_active():
935 font_widget.set_sensitive(False)
936 font_widget = None
937 else:
938 font_widget.set_sensitive(True)
939 self.on_preference_widget_font_set(font_widget, 'conversation_font')
941 def draw_color_widgets(self):
942 col_to_widget = {'inmsgcolor': 'incoming_nick_colorbutton',
943 'outmsgcolor': 'outgoing_nick_colorbutton',
944 'inmsgtxtcolor': ['incoming_msg_colorbutton',
945 'incoming_msg_checkbutton'],
946 'outmsgtxtcolor': ['outgoing_msg_colorbutton',
947 'outgoing_msg_checkbutton'],
948 'statusmsgcolor': 'status_msg_colorbutton',
949 'urlmsgcolor': 'url_msg_colorbutton'}
950 for c in col_to_widget:
951 col = gajim.config.get(c)
952 if col:
953 if isinstance(col_to_widget[c], list):
954 self.xml.get_object(col_to_widget[c][0]).set_color(
955 gtk.gdk.color_parse(col))
956 self.xml.get_object(col_to_widget[c][0]).set_sensitive(True)
957 self.xml.get_object(col_to_widget[c][1]).set_active(True)
958 else:
959 self.xml.get_object(col_to_widget[c]).set_color(
960 gtk.gdk.color_parse(col))
961 else:
962 if isinstance(col_to_widget[c], list):
963 self.xml.get_object(col_to_widget[c][0]).set_color(
964 gtk.gdk.color_parse('#000000'))
965 self.xml.get_object(col_to_widget[c][0]).set_sensitive(False)
966 self.xml.get_object(col_to_widget[c][1]).set_active(False)
967 else:
968 self.xml.get_object(col_to_widget[c]).set_color(
969 gtk.gdk.color_parse('#000000'))
971 def on_reset_colors_button_clicked(self, widget):
972 col_to_widget = {'inmsgcolor': 'incoming_nick_colorbutton',
973 'outmsgcolor': 'outgoing_nick_colorbutton',
974 'inmsgtxtcolor': 'incoming_msg_colorbutton',
975 'outmsgtxtcolor': 'outgoing_msg_colorbutton',
976 'statusmsgcolor': 'status_msg_colorbutton',
977 'urlmsgcolor': 'url_msg_colorbutton'}
978 for c in col_to_widget:
979 gajim.config.set(c, gajim.interface.default_colors[c])
980 self.draw_color_widgets()
982 self.update_text_tags()
983 gajim.interface.save_config()
985 def _set_color(self, state, widget_name, option):
987 Set color value in prefs and update the UI
989 if state:
990 color = self.xml.get_object(widget_name).get_color()
991 color_string = gtkgui_helpers.make_color_string(color)
992 else:
993 color_string = ''
994 gajim.config.set(option, color_string)
995 gajim.interface.save_config()
997 def on_incoming_msg_checkbutton_toggled(self, widget):
998 state = widget.get_active()
999 self.xml.get_object('incoming_msg_colorbutton').set_sensitive(state)
1000 self._set_color(state, 'incoming_msg_colorbutton', 'inmsgtxtcolor')
1002 def on_outgoing_msg_checkbutton_toggled(self, widget):
1003 state = widget.get_active()
1004 self.xml.get_object('outgoing_msg_colorbutton').set_sensitive(state)
1005 self._set_color(state, 'outgoing_msg_colorbutton', 'outmsgtxtcolor')
1007 def on_auto_away_checkbutton_toggled(self, widget):
1008 self.on_checkbutton_toggled(widget, 'autoaway',
1009 [self.auto_away_time_spinbutton, self.auto_away_message_entry])
1011 def on_auto_away_time_spinbutton_value_changed(self, widget):
1012 aat = widget.get_value_as_int()
1013 gajim.config.set('autoawaytime', aat)
1014 gajim.interface.sleeper = common.sleepy.Sleepy(
1015 gajim.config.get('autoawaytime') * 60,
1016 gajim.config.get('autoxatime') * 60)
1017 gajim.interface.save_config()
1019 def on_auto_away_message_entry_changed(self, widget):
1020 gajim.config.set('autoaway_message', widget.get_text().decode('utf-8'))
1022 def on_auto_xa_checkbutton_toggled(self, widget):
1023 self.on_checkbutton_toggled(widget, 'autoxa',
1024 [self.auto_xa_time_spinbutton, self.auto_xa_message_entry])
1026 def on_auto_xa_time_spinbutton_value_changed(self, widget):
1027 axt = widget.get_value_as_int()
1028 gajim.config.set('autoxatime', axt)
1029 gajim.interface.sleeper = common.sleepy.Sleepy(
1030 gajim.config.get('autoawaytime') * 60,
1031 gajim.config.get('autoxatime') * 60)
1032 gajim.interface.save_config()
1034 def on_auto_xa_message_entry_changed(self, widget):
1035 gajim.config.set('autoxa_message', widget.get_text().decode('utf-8'))
1037 def on_prompt_online_status_message_checkbutton_toggled(self, widget):
1038 self.on_checkbutton_toggled(widget, 'ask_online_status')
1040 def on_prompt_offline_status_message_checkbutton_toggled(self, widget):
1041 self.on_checkbutton_toggled(widget, 'ask_offline_status')
1043 def fill_default_msg_treeview(self):
1044 model = self.default_msg_tree.get_model()
1045 model.clear()
1046 status = []
1047 for status_ in gajim.config.get_per('defaultstatusmsg'):
1048 status.append(status_)
1049 status.sort()
1050 for status_ in status:
1051 msg = gajim.config.get_per('defaultstatusmsg', status_, 'message')
1052 msg = helpers.from_one_line(msg)
1053 enabled = gajim.config.get_per('defaultstatusmsg', status_, 'enabled')
1054 iter_ = model.append()
1055 uf_show = helpers.get_uf_show(status_)
1056 model.set(iter_, 0, status_, 1, uf_show, 2, msg, 3, enabled)
1058 def on_default_msg_cell_edited(self, cell, row, new_text):
1059 model = self.default_msg_tree.get_model()
1060 iter_ = model.get_iter_from_string(row)
1061 model.set_value(iter_, 2, new_text)
1063 def default_msg_toggled_cb(self, cell, path):
1064 model = self.default_msg_tree.get_model()
1065 model[path][3] = not model[path][3]
1067 def on_default_msg_treemodel_row_changed(self, model, path, iter_):
1068 status = model[iter_][0]
1069 message = model[iter_][2].decode('utf-8')
1070 message = helpers.to_one_line(message)
1071 gajim.config.set_per('defaultstatusmsg', status, 'enabled',
1072 model[iter_][3])
1073 gajim.config.set_per('defaultstatusmsg', status, 'message', message)
1075 def on_default_status_expander_activate(self, expander):
1076 eventbox = self.xml.get_object('default_status_eventbox')
1077 vbox = self.xml.get_object('status_vbox')
1078 vbox.set_child_packing(eventbox, not expander.get_expanded(), True, 0,
1079 gtk.PACK_START)
1081 def save_status_messages(self, model):
1082 for msg in gajim.config.get_per('statusmsg'):
1083 gajim.config.del_per('statusmsg', msg)
1084 iter_ = model.get_iter_first()
1085 while iter_:
1086 val = model[iter_][0].decode('utf-8')
1087 if model[iter_][1]: # we have a preset message
1088 if not val: # no title, use message text for title
1089 val = model[iter_][1]
1090 gajim.config.add_per('statusmsg', val)
1091 msg = helpers.to_one_line(model[iter_][1].decode('utf-8'))
1092 gajim.config.set_per('statusmsg', val, 'message', msg)
1093 i = 2
1094 # store mood / activity
1095 for subname in ('activity', 'subactivity', 'activity_text',
1096 'mood', 'mood_text'):
1097 gajim.config.set_per('statusmsg', val, subname,
1098 model[iter_][i].decode('utf-8'))
1099 i += 1
1100 iter_ = model.iter_next(iter_)
1101 gajim.interface.save_config()
1103 def on_msg_treemodel_row_changed(self, model, path, iter_):
1104 self.save_status_messages(model)
1106 def on_msg_treemodel_row_deleted(self, model, path):
1107 self.save_status_messages(model)
1109 def on_av_combobox_changed(self, combobox, config_name):
1110 model = combobox.get_model()
1111 active = combobox.get_active()
1112 device = model[active][1].decode('utf-8')
1113 gajim.config.set(config_name, device)
1115 def on_audio_input_combobox_changed(self, widget):
1116 self.on_av_combobox_changed(widget, 'audio_input_device')
1118 def on_audio_output_combobox_changed(self, widget):
1119 self.on_av_combobox_changed(widget, 'audio_output_device')
1121 def on_video_input_combobox_changed(self, widget):
1122 self.on_av_combobox_changed(widget, 'video_input_device')
1124 def on_video_output_combobox_changed(self, widget):
1125 self.on_av_combobox_changed(widget, 'video_output_device')
1127 def on_video_framerate_combobox_changed(self, widget):
1128 self.on_av_combobox_changed(widget, 'video_framerate')
1130 def on_video_size_combobox_changed(self, widget):
1131 self.on_av_combobox_changed(widget, 'video_size')
1133 def on_stun_checkbutton_toggled(self, widget):
1134 self.on_checkbutton_toggled(widget, 'use_stun_server',
1135 [self.xml.get_object('stun_server_entry')])
1137 def stun_server_entry_changed(self, widget):
1138 gajim.config.set('stun_server', widget.get_text().decode('utf-8'))
1140 def on_applications_combobox_changed(self, widget):
1141 if widget.get_active() == 0:
1142 gajim.config.set('autodetect_browser_mailer', True)
1143 self.xml.get_object('custom_apps_frame').hide()
1144 elif widget.get_active() == 1:
1145 gajim.config.set('autodetect_browser_mailer', False)
1146 self.xml.get_object('custom_apps_frame').show()
1147 gajim.interface.save_config()
1149 def on_custom_browser_entry_changed(self, widget):
1150 gajim.config.set('custombrowser', widget.get_text().decode('utf-8'))
1151 gajim.interface.save_config()
1153 def on_custom_mail_client_entry_changed(self, widget):
1154 gajim.config.set('custommailapp', widget.get_text().decode('utf-8'))
1155 gajim.interface.save_config()
1157 def on_custom_file_manager_entry_changed(self, widget):
1158 gajim.config.set('custom_file_manager', widget.get_text().decode('utf-8'))
1159 gajim.interface.save_config()
1161 def on_log_show_changes_checkbutton_toggled(self, widget):
1162 self.on_checkbutton_toggled(widget, 'log_contact_status_changes')
1164 def on_log_encrypted_chats_checkbutton_toggled(self, widget):
1165 widget.set_inconsistent(False)
1166 self.on_per_account_checkbutton_toggled(widget, 'log_encrypted_sessions')
1168 def on_send_os_info_checkbutton_toggled(self, widget):
1169 widget.set_inconsistent(False)
1170 self.on_per_account_checkbutton_toggled(widget, 'send_os_info')
1172 def on_send_idle_time_checkbutton_toggled(self, widget):
1173 widget.set_inconsistent(False)
1174 self.on_per_account_checkbutton_toggled(widget, 'send_idle_time')
1176 def on_check_default_client_checkbutton_toggled(self, widget):
1177 self.on_checkbutton_toggled(widget, 'check_if_gajim_is_default')
1179 def on_notify_gmail_checkbutton_toggled(self, widget):
1180 self.on_checkbutton_toggled(widget, 'notify_on_new_gmail_email')
1182 def on_notify_gmail_extra_checkbutton_toggled(self, widget):
1183 self.on_checkbutton_toggled(widget, 'notify_on_new_gmail_email_extra')
1185 def fill_msg_treeview(self):
1186 self.xml.get_object('delete_msg_button').set_sensitive(False)
1187 model = self.msg_tree.get_model()
1188 model.clear()
1189 preset_status = []
1190 for msg_name in gajim.config.get_per('statusmsg'):
1191 if msg_name.startswith('_last_'):
1192 continue
1193 preset_status.append(msg_name)
1194 preset_status.sort()
1195 for msg_name in preset_status:
1196 msg_text = gajim.config.get_per('statusmsg', msg_name, 'message')
1197 msg_text = helpers.from_one_line(msg_text)
1198 activity = gajim.config.get_per('statusmsg', msg_name, 'activity')
1199 subactivity = gajim.config.get_per('statusmsg', msg_name,
1200 'subactivity')
1201 activity_text = gajim.config.get_per('statusmsg', msg_name,
1202 'activity_text')
1203 mood = gajim.config.get_per('statusmsg', msg_name, 'mood')
1204 mood_text = gajim.config.get_per('statusmsg', msg_name, 'mood_text')
1205 iter_ = model.append()
1206 model.set(iter_, 0, msg_name, 1, msg_text, 2, activity, 3,
1207 subactivity, 4, activity_text, 5, mood, 6, mood_text)
1209 def on_msg_cell_edited(self, cell, row, new_text):
1210 model = self.msg_tree.get_model()
1211 iter_ = model.get_iter_from_string(row)
1212 model.set_value(iter_, 0, new_text)
1214 def on_msg_treeview_cursor_changed(self, widget, data = None):
1215 (model, iter_) = self.msg_tree.get_selection().get_selected()
1216 if not iter_:
1217 return
1218 self.xml.get_object('delete_msg_button').set_sensitive(True)
1219 buf = self.xml.get_object('msg_textview').get_buffer()
1220 msg = model[iter_][1]
1221 buf.set_text(msg)
1223 def on_new_msg_button_clicked(self, widget, data = None):
1224 model = self.msg_tree.get_model()
1225 iter_ = model.append()
1226 model.set(iter_, 0, _('status message title'), 1, _('status message text'))
1227 self.msg_tree.set_cursor(model.get_path(iter_))
1229 def on_delete_msg_button_clicked(self, widget, data = None):
1230 (model, iter_) = self.msg_tree.get_selection().get_selected()
1231 if not iter_:
1232 return
1233 buf = self.xml.get_object('msg_textview').get_buffer()
1234 model.remove(iter_)
1235 buf.set_text('')
1236 self.xml.get_object('delete_msg_button').set_sensitive(False)
1238 def on_msg_textview_changed(self, widget, data = None):
1239 (model, iter_) = self.msg_tree.get_selection().get_selected()
1240 if not iter_:
1241 return
1242 buf = self.xml.get_object('msg_textview').get_buffer()
1243 first_iter, end_iter = buf.get_bounds()
1244 model.set_value(iter_, 1, buf.get_text(first_iter, end_iter))
1246 def on_msg_treeview_key_press_event(self, widget, event):
1247 if event.keyval == gtk.keysyms.Delete:
1248 self.on_delete_msg_button_clicked(widget)
1250 def on_open_advanced_editor_button_clicked(self, widget, data = None):
1251 if 'advanced_config' in gajim.interface.instances:
1252 gajim.interface.instances['advanced_config'].window.present()
1253 else:
1254 gajim.interface.instances['advanced_config'] = \
1255 dialogs.AdvancedConfigurationWindow()
1257 #---------- ManageProxiesWindow class -------------#
1258 class ManageProxiesWindow:
1259 def __init__(self):
1260 self.xml = gtkgui_helpers.get_gtk_builder('manage_proxies_window.ui')
1261 self.window = self.xml.get_object('manage_proxies_window')
1262 self.window.set_transient_for(gajim.interface.roster.window)
1263 self.proxies_treeview = self.xml.get_object('proxies_treeview')
1264 self.proxyname_entry = self.xml.get_object('proxyname_entry')
1265 self.proxytype_combobox = self.xml.get_object('proxytype_combobox')
1267 self.init_list()
1268 self.block_signal = False
1269 self.xml.connect_signals(self)
1270 self.window.show_all()
1271 # hide the BOSH fields by default
1272 self.show_bosh_fields()
1274 def show_bosh_fields(self, show=True):
1275 if show:
1276 self.xml.get_object('boshuri_entry').show()
1277 self.xml.get_object('boshuri_label').show()
1278 self.xml.get_object('boshuseproxy_checkbutton').show()
1279 else:
1280 cb = self.xml.get_object('boshuseproxy_checkbutton')
1281 cb.hide()
1282 cb.set_active(True)
1283 self.on_boshuseproxy_checkbutton_toggled(cb)
1284 self.xml.get_object('boshuri_entry').hide()
1285 self.xml.get_object('boshuri_label').hide()
1288 def fill_proxies_treeview(self):
1289 model = self.proxies_treeview.get_model()
1290 model.clear()
1291 iter_ = model.append()
1292 model.set(iter_, 0, _('None'))
1293 for p in gajim.config.get_per('proxies'):
1294 iter_ = model.append()
1295 model.set(iter_, 0, p)
1297 def init_list(self):
1298 self.xml.get_object('remove_proxy_button').set_sensitive(False)
1299 self.proxytype_combobox.set_sensitive(False)
1300 self.xml.get_object('proxy_table').set_sensitive(False)
1301 model = gtk.ListStore(str)
1302 self.proxies_treeview.set_model(model)
1303 col = gtk.TreeViewColumn('Proxies')
1304 self.proxies_treeview.append_column(col)
1305 renderer = gtk.CellRendererText()
1306 col.pack_start(renderer, True)
1307 col.set_attributes(renderer, text = 0)
1308 self.fill_proxies_treeview()
1309 self.xml.get_object('proxytype_combobox').set_active(0)
1311 def on_manage_proxies_window_destroy(self, widget):
1312 if 'accounts' in gajim.interface.instances:
1313 gajim.interface.instances['accounts'].\
1314 update_proxy_list()
1315 del gajim.interface.instances['manage_proxies']
1317 def on_add_proxy_button_clicked(self, widget):
1318 model = self.proxies_treeview.get_model()
1319 proxies = gajim.config.get_per('proxies')
1320 i = 1
1321 while ('proxy' + unicode(i)) in proxies:
1322 i += 1
1323 iter_ = model.append()
1324 model.set(iter_, 0, 'proxy' + unicode(i))
1325 gajim.config.add_per('proxies', 'proxy' + unicode(i))
1326 self.proxies_treeview.set_cursor(model.get_path(iter_))
1328 def on_remove_proxy_button_clicked(self, widget):
1329 (model, iter_) = self.proxies_treeview.get_selection().get_selected()
1330 if not iter_:
1331 return
1332 proxy = model[iter_][0].decode('utf-8')
1333 model.remove(iter_)
1334 gajim.config.del_per('proxies', proxy)
1335 self.xml.get_object('remove_proxy_button').set_sensitive(False)
1336 self.block_signal = True
1337 self.on_proxies_treeview_cursor_changed(self.proxies_treeview)
1338 self.block_signal = False
1340 def on_close_button_clicked(self, widget):
1341 self.window.destroy()
1343 def on_useauth_checkbutton_toggled(self, widget):
1344 if self.block_signal:
1345 return
1346 act = widget.get_active()
1347 proxy = self.proxyname_entry.get_text().decode('utf-8')
1348 gajim.config.set_per('proxies', proxy, 'useauth', act)
1349 self.xml.get_object('proxyuser_entry').set_sensitive(act)
1350 self.xml.get_object('proxypass_entry').set_sensitive(act)
1352 def on_boshuseproxy_checkbutton_toggled(self, widget):
1353 if self.block_signal:
1354 return
1355 act = widget.get_active()
1356 proxy = self.proxyname_entry.get_text().decode('utf-8')
1357 gajim.config.set_per('proxies', proxy, 'bosh_useproxy', act)
1358 self.xml.get_object('proxyhost_entry').set_sensitive(act)
1359 self.xml.get_object('proxyport_entry').set_sensitive(act)
1361 def on_proxies_treeview_cursor_changed(self, widget):
1362 #FIXME: check if off proxy settings are correct (see
1363 # http://trac.gajim.org/changeset/1921#file2 line 1221
1364 proxyhost_entry = self.xml.get_object('proxyhost_entry')
1365 proxyport_entry = self.xml.get_object('proxyport_entry')
1366 proxyuser_entry = self.xml.get_object('proxyuser_entry')
1367 proxypass_entry = self.xml.get_object('proxypass_entry')
1368 boshuri_entry = self.xml.get_object('boshuri_entry')
1369 useauth_checkbutton = self.xml.get_object('useauth_checkbutton')
1370 boshuseproxy_checkbutton = self.xml.get_object('boshuseproxy_checkbutton')
1371 self.block_signal = True
1372 proxyhost_entry.set_text('')
1373 proxyport_entry.set_text('')
1374 proxyuser_entry.set_text('')
1375 proxypass_entry.set_text('')
1376 boshuri_entry.set_text('')
1378 #boshuseproxy_checkbutton.set_active(False)
1379 #self.on_boshuseproxy_checkbutton_toggled(boshuseproxy_checkbutton)
1381 #useauth_checkbutton.set_active(False)
1382 #self.on_useauth_checkbutton_toggled(useauth_checkbutton)
1384 (model, iter_) = widget.get_selection().get_selected()
1385 if not iter_:
1386 self.xml.get_object('proxyname_entry').set_text('')
1387 self.xml.get_object('proxytype_combobox').set_sensitive(False)
1388 self.xml.get_object('proxy_table').set_sensitive(False)
1389 self.block_signal = False
1390 return
1392 proxy = model[iter_][0]
1393 self.xml.get_object('proxyname_entry').set_text(proxy)
1395 if proxy == _('None'): # special proxy None
1396 self.show_bosh_fields(False)
1397 self.proxyname_entry.set_editable(False)
1398 self.xml.get_object('remove_proxy_button').set_sensitive(False)
1399 self.xml.get_object('proxytype_combobox').set_sensitive(False)
1400 self.xml.get_object('proxy_table').set_sensitive(False)
1401 else:
1402 proxytype = gajim.config.get_per('proxies', proxy, 'type')
1404 self.show_bosh_fields(proxytype=='bosh')
1406 self.proxyname_entry.set_editable(True)
1407 self.xml.get_object('remove_proxy_button').set_sensitive(True)
1408 self.xml.get_object('proxytype_combobox').set_sensitive(True)
1409 self.xml.get_object('proxy_table').set_sensitive(True)
1410 proxyhost_entry.set_text(gajim.config.get_per('proxies', proxy,
1411 'host'))
1412 proxyport_entry.set_text(unicode(gajim.config.get_per('proxies',
1413 proxy, 'port')))
1414 proxyuser_entry.set_text(gajim.config.get_per('proxies', proxy,
1415 'user'))
1416 proxypass_entry.set_text(gajim.config.get_per('proxies', proxy,
1417 'pass'))
1418 boshuri_entry.set_text(gajim.config.get_per('proxies', proxy,
1419 'bosh_uri'))
1420 types = ['http', 'socks5', 'bosh']
1421 self.proxytype_combobox.set_active(types.index(proxytype))
1422 boshuseproxy_checkbutton.set_active(
1423 gajim.config.get_per('proxies', proxy, 'bosh_useproxy'))
1424 useauth_checkbutton.set_active(
1425 gajim.config.get_per('proxies', proxy, 'useauth'))
1426 self.block_signal = False
1428 def on_proxies_treeview_key_press_event(self, widget, event):
1429 if event.keyval == gtk.keysyms.Delete:
1430 self.on_remove_proxy_button_clicked(widget)
1432 def on_proxyname_entry_changed(self, widget):
1433 if self.block_signal:
1434 return
1435 (model, iter_) = self.proxies_treeview.get_selection().get_selected()
1436 if not iter_:
1437 return
1438 old_name = model.get_value(iter_, 0).decode('utf-8')
1439 new_name = widget.get_text().decode('utf-8')
1440 if new_name == '':
1441 return
1442 if new_name == old_name:
1443 return
1444 config = gajim.config.get_per('proxies', old_name)
1445 gajim.config.del_per('proxies', old_name)
1446 gajim.config.add_per('proxies', new_name)
1447 for option in config:
1448 gajim.config.set_per('proxies', new_name, option,
1449 config[option][common.config.OPT_VAL])
1450 model.set_value(iter_, 0, new_name)
1452 def on_proxytype_combobox_changed(self, widget):
1453 if self.block_signal:
1454 return
1455 types = ['http', 'socks5', 'bosh']
1456 type_ = self.proxytype_combobox.get_active()
1457 self.show_bosh_fields(types[type_]=='bosh')
1458 proxy = self.proxyname_entry.get_text().decode('utf-8')
1459 gajim.config.set_per('proxies', proxy, 'type', types[type_])
1461 def on_proxyhost_entry_changed(self, widget):
1462 if self.block_signal:
1463 return
1464 value = widget.get_text().decode('utf-8')
1465 proxy = self.proxyname_entry.get_text().decode('utf-8')
1466 gajim.config.set_per('proxies', proxy, 'host', value)
1468 def on_proxyport_entry_changed(self, widget):
1469 if self.block_signal:
1470 return
1471 value = widget.get_text().decode('utf-8')
1472 proxy = self.proxyname_entry.get_text().decode('utf-8')
1473 gajim.config.set_per('proxies', proxy, 'port', value)
1475 def on_proxyuser_entry_changed(self, widget):
1476 if self.block_signal:
1477 return
1478 value = widget.get_text().decode('utf-8')
1479 proxy = self.proxyname_entry.get_text().decode('utf-8')
1480 gajim.config.set_per('proxies', proxy, 'user', value)
1482 def on_boshuri_entry_changed(self, widget):
1483 if self.block_signal:
1484 return
1485 value = widget.get_text().decode('utf-8')
1486 proxy = self.proxyname_entry.get_text().decode('utf-8')
1487 gajim.config.set_per('proxies', proxy, 'bosh_uri', value)
1489 def on_proxypass_entry_changed(self, widget):
1490 if self.block_signal:
1491 return
1492 value = widget.get_text().decode('utf-8')
1493 proxy = self.proxyname_entry.get_text().decode('utf-8')
1494 gajim.config.set_per('proxies', proxy, 'pass', value)
1497 #---------- AccountsWindow class -------------#
1498 class AccountsWindow:
1500 Class for accounts window: list of accounts
1503 def on_accounts_window_destroy(self, widget):
1504 del gajim.interface.instances['accounts']
1506 def on_close_button_clicked(self, widget):
1507 self.check_resend_relog()
1508 self.window.destroy()
1510 def __init__(self):
1511 self.xml = gtkgui_helpers.get_gtk_builder('accounts_window.ui')
1512 self.window = self.xml.get_object('accounts_window')
1513 self.window.set_transient_for(gajim.interface.roster.window)
1514 self.accounts_treeview = self.xml.get_object('accounts_treeview')
1515 self.remove_button = self.xml.get_object('remove_button')
1516 self.rename_button = self.xml.get_object('rename_button')
1517 path_to_kbd_input_img = gtkgui_helpers.get_icon_path('gajim-kbd_input')
1518 img = self.xml.get_object('rename_image')
1519 img.set_from_file(path_to_kbd_input_img)
1520 self.notebook = self.xml.get_object('notebook')
1521 # Name
1522 model = gtk.ListStore(str)
1523 self.accounts_treeview.set_model(model)
1524 # column
1525 renderer = gtk.CellRendererText()
1526 self.accounts_treeview.insert_column_with_attributes(-1, _('Name'),
1527 renderer, text=0)
1529 self.current_account = None
1530 # When we fill info, we don't want to handle the changed signals
1531 self.ignore_events = False
1532 self.need_relogin = False
1533 self.resend_presence = False
1535 self.update_proxy_list()
1536 self.xml.connect_signals(self)
1537 self.init_accounts()
1538 self.window.show_all()
1540 # Merge accounts
1541 st = gajim.config.get('mergeaccounts')
1542 checkbutton = self.xml.get_object('merge_checkbutton')
1543 checkbutton.set_active(st)
1544 # prevent roster redraws by connecting the signal after button state is
1545 # set
1546 checkbutton.connect('toggled', self.on_merge_checkbutton_toggled)
1548 self.avahi_available = True
1549 try:
1550 import avahi
1551 except ImportError:
1552 self.avahi_available = False
1554 def on_accounts_window_key_press_event(self, widget, event):
1555 if event.keyval == gtk.keysyms.Escape:
1556 self.check_resend_relog()
1557 self.window.destroy()
1559 def select_account(self, account):
1560 model = self.accounts_treeview.get_model()
1561 iter_ = model.get_iter_root()
1562 while iter_:
1563 acct = model[iter_][0].decode('utf-8')
1564 if account == acct:
1565 self.accounts_treeview.set_cursor(model.get_path(iter_))
1566 return
1567 iter_ = model.iter_next(iter_)
1569 def init_accounts(self):
1571 Initialize listStore with existing accounts
1573 self.remove_button.set_sensitive(False)
1574 self.rename_button.set_sensitive(False)
1575 self.current_account = None
1576 model = self.accounts_treeview.get_model()
1577 model.clear()
1578 for account in gajim.config.get_per('accounts'):
1579 iter_ = model.append()
1580 model.set(iter_, 0, account)
1582 def resend(self, account):
1583 if not account in gajim.connections:
1584 return
1585 show = gajim.SHOW_LIST[gajim.connections[account].connected]
1586 status = gajim.connections[account].status
1587 gajim.connections[account].change_status(show, status)
1589 def check_resend_relog(self):
1590 if self.need_relogin and self.current_account == gajim.ZEROCONF_ACC_NAME:
1591 if gajim.ZEROCONF_ACC_NAME in gajim.connections:
1592 gajim.connections[gajim.ZEROCONF_ACC_NAME].update_details()
1593 return
1595 elif self.need_relogin and self.current_account and \
1596 gajim.connections[self.current_account].connected > 0:
1597 def login(account, show_before, status_before):
1599 Login with previous status
1601 # first make sure connection is really closed,
1602 # 0.5 may not be enough
1603 gajim.connections[account].disconnect(True)
1604 gajim.interface.roster.send_status(account, show_before,
1605 status_before)
1607 def relog(account):
1608 self.dialog.destroy()
1609 show_before = gajim.SHOW_LIST[gajim.connections[account].connected]
1610 status_before = gajim.connections[account].status
1611 gajim.interface.roster.send_status(account, 'offline',
1612 _('Be right back.'))
1613 gobject.timeout_add(500, login, account, show_before, status_before)
1615 def on_yes(checked, account):
1616 relog(account)
1617 def on_no(account):
1618 if self.resend_presence:
1619 self.resend(account)
1620 if self.current_account in gajim.connections:
1621 self.dialog = dialogs.YesNoDialog(_('Relogin now?'),
1622 _('If you want all the changes to apply instantly, '
1623 'you must relogin.'), on_response_yes=(on_yes,
1624 self.current_account), on_response_no=(on_no,
1625 self.current_account))
1626 elif self.resend_presence:
1627 self.resend(self.current_account)
1629 self.need_relogin = False
1630 self.resend_presence = False
1632 def on_accounts_treeview_cursor_changed(self, widget):
1634 Activate modify buttons when a row is selected, update accounts info
1636 sel = self.accounts_treeview.get_selection()
1637 (model, iter_) = sel.get_selected()
1638 if iter_:
1639 account = model[iter_][0].decode('utf-8')
1640 else:
1641 account = None
1642 if self.current_account and self.current_account == account:
1643 # We're comming back to our current account, no need to update widgets
1644 return
1645 # Save config for previous account if needed cause focus_out event is
1646 # called after the changed event
1647 if self.current_account and self.window.get_focus():
1648 focused_widget = self.window.get_focus()
1649 focused_widget_name = focused_widget.get_name()
1650 if focused_widget_name in ('jid_entry1', 'resource_entry1',
1651 'custom_port_entry', 'cert_entry1'):
1652 if focused_widget_name == 'jid_entry1':
1653 func = self.on_jid_entry1_focus_out_event
1654 elif focused_widget_name == 'resource_entry1':
1655 func = self.on_resource_entry1_focus_out_event
1656 elif focused_widget_name == 'custom_port_entry':
1657 func = self.on_custom_port_entry_focus_out_event
1658 elif focused_widget_name == 'cert_entry1':
1659 func = self.on_cert_entry1_focus_out_event
1660 if func(focused_widget, None):
1661 # Error detected in entry, don't change account, re-put cursor on
1662 # previous row
1663 self.select_account(self.current_account)
1664 return True
1665 self.window.set_focus(widget)
1667 self.check_resend_relog()
1669 if account:
1670 self.remove_button.set_sensitive(True)
1671 self.rename_button.set_sensitive(True)
1672 else:
1673 self.remove_button.set_sensitive(False)
1674 self.rename_button.set_sensitive(False)
1675 if iter_:
1676 self.current_account = account
1677 if account == gajim.ZEROCONF_ACC_NAME:
1678 self.remove_button.set_sensitive(False)
1679 self.init_account()
1680 self.update_proxy_list()
1682 def on_browse_for_client_cert_button_clicked(self, widget, data=None):
1683 def on_ok(widget, path_to_clientcert_file):
1684 self.dialog.destroy()
1685 if not path_to_clientcert_file:
1686 return
1687 self.xml.get_object('cert_entry1').set_text(path_to_clientcert_file)
1688 gajim.config.set_per('accounts', self.current_account,
1689 'client_cert', path_to_clientcert_file)
1691 def on_cancel(widget):
1692 self.dialog.destroy()
1694 path_to_clientcert_file = self.xml.get_object('cert_entry1').get_text()
1695 self.dialog = dialogs.ClientCertChooserDialog(path_to_clientcert_file,
1696 on_ok, on_cancel)
1698 def update_proxy_list(self):
1699 if self.current_account:
1700 our_proxy = gajim.config.get_per('accounts', self.current_account,
1701 'proxy')
1702 else:
1703 our_proxy = ''
1705 if not our_proxy:
1706 our_proxy = _('None')
1707 proxy_combobox = self.xml.get_object('proxies_combobox1')
1708 model = gtk.ListStore(str)
1709 proxy_combobox.set_model(model)
1710 l = gajim.config.get_per('proxies')
1711 l.insert(0, _('None'))
1712 for i in xrange(len(l)):
1713 model.append([l[i]])
1714 if our_proxy == l[i]:
1715 proxy_combobox.set_active(i)
1717 def init_account(self):
1718 if not self.current_account:
1719 self.notebook.set_current_page(0)
1720 return
1721 if gajim.config.get_per('accounts', self.current_account, 'is_zeroconf'):
1722 self.ignore_events = True
1723 self.init_zeroconf_account()
1724 self.ignore_events = False
1725 self.notebook.set_current_page(2)
1726 return
1727 self.ignore_events = True
1728 self.init_normal_account()
1729 self.ignore_events = False
1730 self.notebook.set_current_page(1)
1732 def init_zeroconf_account(self):
1733 active = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1734 'active')
1735 self.xml.get_object('enable_zeroconf_checkbutton2').set_active(active)
1736 if not gajim.HAVE_ZEROCONF:
1737 self.xml.get_object('enable_zeroconf_checkbutton2').set_sensitive(
1738 False)
1739 self.xml.get_object('zeroconf_notebook').set_sensitive(active)
1740 # General tab
1741 st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1742 'autoconnect')
1743 self.xml.get_object('autoconnect_checkbutton2').set_active(st)
1745 list_no_log_for = gajim.config.get_per('accounts',
1746 gajim.ZEROCONF_ACC_NAME, 'no_log_for').split()
1747 if gajim.ZEROCONF_ACC_NAME in list_no_log_for:
1748 self.xml.get_object('log_history_checkbutton2').set_active(0)
1749 else:
1750 self.xml.get_object('log_history_checkbutton2').set_active(1)
1752 st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1753 'sync_with_global_status')
1754 self.xml.get_object('sync_with_global_status_checkbutton2').set_active(st)
1756 st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1757 'use_custom_host')
1758 self.xml.get_object('custom_port_checkbutton2').set_active(st)
1759 self.xml.get_object('custom_port_entry2').set_sensitive(st)
1761 st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1762 'custom_port')
1763 if not st:
1764 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME,
1765 'custom_port', '5298')
1766 st = '5298'
1767 self.xml.get_object('custom_port_entry2').set_text(str(st))
1769 # Personal tab
1770 gpg_key_label = self.xml.get_object('gpg_key_label2')
1771 if gajim.ZEROCONF_ACC_NAME in gajim.connections and \
1772 gajim.connections[gajim.ZEROCONF_ACC_NAME].gpg:
1773 self.xml.get_object('gpg_choose_button2').set_sensitive(True)
1774 self.init_account_gpg()
1775 else:
1776 gpg_key_label.set_text(_('OpenPGP is not usable on this computer'))
1777 self.xml.get_object('gpg_choose_button2').set_sensitive(False)
1779 for opt in ('first_name', 'last_name', 'jabber_id', 'email'):
1780 st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
1781 'zeroconf_' + opt)
1782 self.xml.get_object(opt + '_entry2').set_text(st)
1784 def init_account_gpg(self):
1785 account = self.current_account
1786 keyid = gajim.config.get_per('accounts', account, 'keyid')
1787 keyname = gajim.config.get_per('accounts', account, 'keyname')
1788 use_gpg_agent = gajim.config.get('use_gpg_agent')
1790 if account == gajim.ZEROCONF_ACC_NAME:
1791 widget_name_add = '2'
1792 else:
1793 widget_name_add = '1'
1795 gpg_key_label = self.xml.get_object('gpg_key_label' + widget_name_add)
1796 gpg_name_label = self.xml.get_object('gpg_name_label' + widget_name_add)
1797 use_gpg_agent_checkbutton = self.xml.get_object(
1798 'use_gpg_agent_checkbutton' + widget_name_add)
1800 if not keyid:
1801 use_gpg_agent_checkbutton.set_sensitive(False)
1802 gpg_key_label.set_text(_('No key selected'))
1803 gpg_name_label.set_text('')
1804 return
1806 gpg_key_label.set_text(keyid)
1807 gpg_name_label.set_text(keyname)
1808 use_gpg_agent_checkbutton.set_sensitive(True)
1809 use_gpg_agent_checkbutton.set_active(use_gpg_agent)
1811 def draw_normal_jid(self):
1812 account = self.current_account
1813 self.ignore_events = True
1814 active = gajim.config.get_per('accounts', account, 'active')
1815 self.xml.get_object('enable_checkbutton1').set_active(active)
1816 self.xml.get_object('normal_notebook1').set_sensitive(active)
1817 if gajim.config.get_per('accounts', account, 'anonymous_auth'):
1818 self.xml.get_object('anonymous_checkbutton1').set_active(True)
1819 self.xml.get_object('jid_label1').set_text(_('Server:'))
1820 save_password = self.xml.get_object('save_password_checkbutton1')
1821 save_password.set_active(False)
1822 save_password.set_sensitive(False)
1823 password_entry = self.xml.get_object('password_entry1')
1824 password_entry.set_text('')
1825 password_entry.set_sensitive(False)
1826 jid = gajim.config.get_per('accounts', account, 'hostname')
1827 else:
1828 self.xml.get_object('anonymous_checkbutton1').set_active(False)
1829 self.xml.get_object('jid_label1').set_text(_('Jabber ID:'))
1830 savepass = gajim.config.get_per('accounts', account, 'savepass')
1831 save_password = self.xml.get_object('save_password_checkbutton1')
1832 save_password.set_sensitive(True)
1833 save_password.set_active(savepass)
1834 password_entry = self.xml.get_object('password_entry1')
1835 if savepass:
1836 passstr = passwords.get_password(account) or ''
1837 password_entry.set_sensitive(True)
1838 else:
1839 passstr = ''
1840 password_entry.set_sensitive(False)
1841 password_entry.set_text(passstr)
1843 jid = gajim.config.get_per('accounts', account, 'name') \
1844 + '@' + gajim.config.get_per('accounts', account, 'hostname')
1845 self.xml.get_object('jid_entry1').set_text(jid)
1846 self.ignore_events = False
1848 def init_normal_account(self):
1849 account = self.current_account
1850 # Account tab
1851 self.draw_normal_jid()
1852 self.xml.get_object('resource_entry1').set_text(gajim.config.get_per(
1853 'accounts', account, 'resource'))
1855 client_cert = gajim.config.get_per('accounts', account, 'client_cert')
1856 self.xml.get_object('cert_entry1').set_text(client_cert)
1858 self.xml.get_object('adjust_priority_with_status_checkbutton1').\
1859 set_active(gajim.config.get_per('accounts', account,
1860 'adjust_priority_with_status'))
1861 spinbutton = self.xml.get_object('priority_spinbutton1')
1862 if gajim.config.get('enable_negative_priority'):
1863 spinbutton.set_range(-128, 127)
1864 else:
1865 spinbutton.set_range(0, 127)
1866 spinbutton.set_value(gajim.config.get_per('accounts', account,
1867 'priority'))
1869 # Connection tab
1870 use_env_http_proxy = gajim.config.get_per('accounts', account,
1871 'use_env_http_proxy')
1872 self.xml.get_object('use_env_http_proxy_checkbutton1').set_active(
1873 use_env_http_proxy)
1874 self.xml.get_object('proxy_hbox1').set_sensitive(not use_env_http_proxy)
1876 warn_when_insecure_ssl = gajim.config.get_per('accounts', account,
1877 'warn_when_insecure_ssl_connection')
1878 self.xml.get_object('warn_when_insecure_connection_checkbutton1').\
1879 set_active(warn_when_insecure_ssl)
1881 self.xml.get_object('send_keepalive_checkbutton1').set_active(
1882 gajim.config.get_per('accounts', account, 'keep_alives_enabled'))
1884 use_custom_host = gajim.config.get_per('accounts', account,
1885 'use_custom_host')
1886 self.xml.get_object('custom_host_port_checkbutton1').set_active(
1887 use_custom_host)
1888 custom_host = gajim.config.get_per('accounts', account, 'custom_host')
1889 if not custom_host:
1890 custom_host = gajim.config.get_per('accounts', account, 'hostname')
1891 gajim.config.set_per('accounts', account, 'custom_host', custom_host)
1892 self.xml.get_object('custom_host_entry1').set_text(custom_host)
1893 custom_port = gajim.config.get_per('accounts', account, 'custom_port')
1894 if not custom_port:
1895 custom_port = 5222
1896 gajim.config.set_per('accounts', account, 'custom_port', custom_port)
1897 self.xml.get_object('custom_port_entry1').set_text(unicode(custom_port))
1899 # Personal tab
1900 gpg_key_label = self.xml.get_object('gpg_key_label1')
1901 if gajim.HAVE_GPG:
1902 self.xml.get_object('gpg_choose_button1').set_sensitive(True)
1903 self.init_account_gpg()
1904 else:
1905 gpg_key_label.set_text(_('OpenPGP is not usable on this computer'))
1906 self.xml.get_object('gpg_choose_button1').set_sensitive(False)
1908 # General tab
1909 self.xml.get_object('autoconnect_checkbutton1').set_active(gajim.config.\
1910 get_per('accounts', account, 'autoconnect'))
1911 self.xml.get_object('autoreconnect_checkbutton1').set_active(gajim.
1912 config.get_per('accounts', account, 'autoreconnect'))
1914 list_no_log_for = gajim.config.get_per('accounts', account,
1915 'no_log_for').split()
1916 if account in list_no_log_for:
1917 self.xml.get_object('log_history_checkbutton1').set_active(False)
1918 else:
1919 self.xml.get_object('log_history_checkbutton1').set_active(True)
1921 self.xml.get_object('sync_with_global_status_checkbutton1').set_active(
1922 gajim.config.get_per('accounts', account, 'sync_with_global_status'))
1923 self.xml.get_object('use_ft_proxies_checkbutton1').set_active(
1924 gajim.config.get_per('accounts', account, 'use_ft_proxies'))
1926 def on_add_button_clicked(self, widget):
1928 When add button is clicked: open an account information window
1930 if 'account_creation_wizard' in gajim.interface.instances:
1931 gajim.interface.instances['account_creation_wizard'].window.present()
1932 else:
1933 gajim.interface.instances['account_creation_wizard'] = \
1934 AccountCreationWizardWindow()
1936 def on_remove_button_clicked(self, widget):
1938 When delete button is clicked: Remove an account from the listStore and
1939 from the config file
1941 if not self.current_account:
1942 return
1943 account = self.current_account
1944 if len(gajim.events.get_events(account)):
1945 dialogs.ErrorDialog(_('Unread events'),
1946 _('Read all pending events before removing this account.'))
1947 return
1949 if gajim.config.get_per('accounts', account, 'is_zeroconf'):
1950 # Should never happen as button is insensitive
1951 return
1953 win_opened = False
1954 if gajim.interface.msg_win_mgr.get_controls(acct=account):
1955 win_opened = True
1956 elif account in gajim.interface.instances:
1957 for key in gajim.interface.instances[account]:
1958 if gajim.interface.instances[account][key] and key != \
1959 'remove_account':
1960 win_opened = True
1961 break
1962 # Detect if we have opened windows for this account
1963 def remove(account):
1964 if account in gajim.interface.instances and \
1965 'remove_account' in gajim.interface.instances[account]:
1966 gajim.interface.instances[account]['remove_account'].window.\
1967 present()
1968 else:
1969 if not account in gajim.interface.instances:
1970 gajim.interface.instances[account] = {}
1971 gajim.interface.instances[account]['remove_account'] = \
1972 RemoveAccountWindow(account)
1973 if win_opened:
1974 dialogs.ConfirmationDialog(
1975 _('You have opened chat in account %s') % account,
1976 _('All chat and groupchat windows will be closed. Do you want to '
1977 'continue?'),
1978 on_response_ok = (remove, account))
1979 else:
1980 remove(account)
1982 def on_rename_button_clicked(self, widget):
1983 if not self.current_account:
1984 return
1985 active = gajim.config.get_per('accounts', self.current_account, 'active')
1986 if active and gajim.connections[self.current_account].connected != 0:
1987 dialogs.ErrorDialog(
1988 _('You are currently connected to the server'),
1989 _('To change the account name, you must be disconnected.'))
1990 return
1991 if len(gajim.events.get_events(self.current_account)):
1992 dialogs.ErrorDialog(_('Unread events'),
1993 _('To change the account name, you must read all pending '
1994 'events.'))
1995 return
1996 # Get the new name
1997 def on_renamed(new_name, old_name):
1998 if new_name in gajim.connections:
1999 dialogs.ErrorDialog(_('Account Name Already Used'),
2000 _('This name is already used by another of your accounts. '
2001 'Please choose another name.'))
2002 return
2003 if (new_name == ''):
2004 dialogs.ErrorDialog(_('Invalid account name'),
2005 _('Account name cannot be empty.'))
2006 return
2007 if new_name.find(' ') != -1:
2008 dialogs.ErrorDialog(_('Invalid account name'),
2009 _('Account name cannot contain spaces.'))
2010 return
2011 if active:
2012 # update variables
2013 gajim.interface.instances[new_name] = gajim.interface.instances[
2014 old_name]
2015 gajim.interface.minimized_controls[new_name] = \
2016 gajim.interface.minimized_controls[old_name]
2017 gajim.nicks[new_name] = gajim.nicks[old_name]
2018 gajim.block_signed_in_notifications[new_name] = \
2019 gajim.block_signed_in_notifications[old_name]
2020 gajim.groups[new_name] = gajim.groups[old_name]
2021 gajim.gc_connected[new_name] = gajim.gc_connected[old_name]
2022 gajim.automatic_rooms[new_name] = gajim.automatic_rooms[old_name]
2023 gajim.newly_added[new_name] = gajim.newly_added[old_name]
2024 gajim.to_be_removed[new_name] = gajim.to_be_removed[old_name]
2025 gajim.sleeper_state[new_name] = gajim.sleeper_state[old_name]
2026 gajim.encrypted_chats[new_name] = gajim.encrypted_chats[old_name]
2027 gajim.last_message_time[new_name] = \
2028 gajim.last_message_time[old_name]
2029 gajim.status_before_autoaway[new_name] = \
2030 gajim.status_before_autoaway[old_name]
2031 gajim.transport_avatar[new_name] = gajim.transport_avatar[old_name]
2032 gajim.gajim_optional_features[new_name] = \
2033 gajim.gajim_optional_features[old_name]
2034 gajim.caps_hash[new_name] = gajim.caps_hash[old_name]
2036 gajim.contacts.change_account_name(old_name, new_name)
2037 gajim.events.change_account_name(old_name, new_name)
2039 # change account variable for chat / gc controls
2040 gajim.interface.msg_win_mgr.change_account_name(old_name, new_name)
2041 # upgrade account variable in opened windows
2042 for kind in ('infos', 'disco', 'gc_config', 'search',
2043 'online_dialog'):
2044 for j in gajim.interface.instances[new_name][kind]:
2045 gajim.interface.instances[new_name][kind][j].account = \
2046 new_name
2048 # ServiceCache object keep old property account
2049 if hasattr(gajim.connections[old_name], 'services_cache'):
2050 gajim.connections[old_name].services_cache.account = new_name
2051 del gajim.interface.instances[old_name]
2052 del gajim.interface.minimized_controls[old_name]
2053 del gajim.nicks[old_name]
2054 del gajim.block_signed_in_notifications[old_name]
2055 del gajim.groups[old_name]
2056 del gajim.gc_connected[old_name]
2057 del gajim.automatic_rooms[old_name]
2058 del gajim.newly_added[old_name]
2059 del gajim.to_be_removed[old_name]
2060 del gajim.sleeper_state[old_name]
2061 del gajim.encrypted_chats[old_name]
2062 del gajim.last_message_time[old_name]
2063 del gajim.status_before_autoaway[old_name]
2064 del gajim.transport_avatar[old_name]
2065 del gajim.gajim_optional_features[old_name]
2066 del gajim.caps_hash[old_name]
2067 gajim.connections[old_name].name = new_name
2068 gajim.connections[old_name].pep_change_account_name(new_name)
2069 gajim.connections[old_name].caps_change_account_name(new_name)
2070 gajim.connections[new_name] = gajim.connections[old_name]
2071 del gajim.connections[old_name]
2072 gajim.config.add_per('accounts', new_name)
2073 old_config = gajim.config.get_per('accounts', old_name)
2074 for opt in old_config:
2075 gajim.config.set_per('accounts', new_name, opt, old_config[opt][1])
2076 gajim.config.del_per('accounts', old_name)
2077 if self.current_account == old_name:
2078 self.current_account = new_name
2079 if old_name == gajim.ZEROCONF_ACC_NAME:
2080 gajim.ZEROCONF_ACC_NAME = new_name
2081 # refresh roster
2082 gajim.interface.roster.setup_and_draw_roster()
2083 self.init_accounts()
2084 self.select_account(new_name)
2086 title = _('Rename Account')
2087 message = _('Enter a new name for account %s') % self.current_account
2088 old_text = self.current_account
2089 dialogs.InputDialog(title, message, old_text, is_modal=False,
2090 ok_handler=(on_renamed, self.current_account))
2092 def option_changed(self, option, value):
2093 return gajim.config.get_per('accounts', self.current_account, option) != \
2094 value
2096 def on_jid_entry1_focus_out_event(self, widget, event):
2097 if self.ignore_events:
2098 return
2099 jid = widget.get_text()
2100 # check if jid is conform to RFC and stringprep it
2101 try:
2102 jid = helpers.parse_jid(jid)
2103 except helpers.InvalidFormat, s:
2104 if not widget.is_focus():
2105 pritext = _('Invalid Jabber ID')
2106 dialogs.ErrorDialog(pritext, str(s))
2107 gobject.idle_add(lambda: widget.grab_focus())
2108 return True
2110 jid_splited = jid.split('@', 1)
2111 if len(jid_splited) != 2 and not gajim.config.get_per('accounts',
2112 self.current_account, 'anonymous_auth'):
2113 if not widget.is_focus():
2114 pritext = _('Invalid Jabber ID')
2115 sectext = _('A Jabber ID must be in the form "user@servername".')
2116 dialogs.ErrorDialog(pritext, sectext)
2117 gobject.idle_add(lambda: widget.grab_focus())
2118 return True
2121 if gajim.config.get_per('accounts', self.current_account,
2122 'anonymous_auth'):
2123 gajim.config.set_per('accounts', self.current_account, 'hostname',
2124 jid_splited[0])
2125 if self.option_changed('hostname', jid_splited[0]):
2126 self.need_relogin = True
2127 else:
2128 if self.option_changed('name', jid_splited[0]) or \
2129 self.option_changed('hostname', jid_splited[1]):
2130 self.need_relogin = True
2132 gajim.config.set_per('accounts', self.current_account, 'name',
2133 jid_splited[0])
2134 gajim.config.set_per('accounts', self.current_account, 'hostname',
2135 jid_splited[1])
2137 def on_cert_entry1_focus_out_event(self, widget, event):
2138 if self.ignore_events:
2139 return
2140 client_cert = widget.get_text()
2141 if self.option_changed('client_cert', client_cert):
2142 self.need_relogin = True
2143 gajim.config.set_per('accounts', self.current_account, 'client_cert',
2144 client_cert)
2146 def on_anonymous_checkbutton1_toggled(self, widget):
2147 if self.ignore_events:
2148 return
2149 active = widget.get_active()
2150 gajim.config.set_per('accounts', self.current_account, 'anonymous_auth',
2151 active)
2152 self.draw_normal_jid()
2154 def on_password_entry1_changed(self, widget):
2155 if self.ignore_events:
2156 return
2157 passwords.save_password(self.current_account, widget.get_text().decode(
2158 'utf-8'))
2160 def on_save_password_checkbutton1_toggled(self, widget):
2161 if self.ignore_events:
2162 return
2163 active = widget.get_active()
2164 password_entry = self.xml.get_object('password_entry1')
2165 password_entry.set_sensitive(active)
2166 gajim.config.set_per('accounts', self.current_account, 'savepass', active)
2167 if active:
2168 password = password_entry.get_text()
2169 passwords.save_password(self.current_account, password)
2170 else:
2171 passwords.save_password(self.current_account, '')
2173 def on_resource_entry1_focus_out_event(self, widget, event):
2174 if self.ignore_events:
2175 return
2176 resource = self.xml.get_object('resource_entry1').get_text().decode(
2177 'utf-8')
2178 try:
2179 resource = helpers.parse_resource(resource)
2180 except helpers.InvalidFormat, s:
2181 if not widget.is_focus():
2182 pritext = _('Invalid Jabber ID')
2183 dialogs.ErrorDialog(pritext, str(s))
2184 gobject.idle_add(lambda: widget.grab_focus())
2185 return True
2187 if self.option_changed('resource', resource):
2188 self.need_relogin = True
2190 gajim.config.set_per('accounts', self.current_account, 'resource',
2191 resource)
2193 def on_adjust_priority_with_status_checkbutton1_toggled(self, widget):
2194 self.xml.get_object('priority_spinbutton1').set_sensitive(
2195 not widget.get_active())
2196 self.on_checkbutton_toggled(widget, 'adjust_priority_with_status',
2197 account = self.current_account)
2199 def on_priority_spinbutton1_value_changed(self, widget):
2200 prio = widget.get_value_as_int()
2202 if self.option_changed('priority', prio):
2203 self.resend_presence = True
2205 gajim.config.set_per('accounts', self.current_account, 'priority', prio)
2207 def on_synchronise_contacts_button1_clicked(self, widget):
2208 try:
2209 dialogs.SynchroniseSelectAccountDialog(self.current_account)
2210 except GajimGeneralException:
2211 # If we showed ErrorDialog, there will not be dialog instance
2212 return
2214 def on_change_password_button1_clicked(self, widget):
2215 def on_changed(new_password):
2216 if new_password is not None:
2217 gajim.connections[self.current_account].change_password(
2218 new_password)
2219 if self.xml.get_object('save_password_checkbutton1').get_active():
2220 self.xml.get_object('password_entry1').set_text(new_password)
2222 try:
2223 dialogs.ChangePasswordDialog(self.current_account, on_changed)
2224 except GajimGeneralException:
2225 # if we showed ErrorDialog, there will not be dialog instance
2226 return
2228 def on_autoconnect_checkbutton_toggled(self, widget):
2229 if self.ignore_events:
2230 return
2231 self.on_checkbutton_toggled(widget, 'autoconnect',
2232 account=self.current_account)
2234 def on_autoreconnect_checkbutton_toggled(self, widget):
2235 if self.ignore_events:
2236 return
2237 self.on_checkbutton_toggled(widget, 'autoreconnect',
2238 account=self.current_account)
2240 def on_log_history_checkbutton_toggled(self, widget):
2241 if self.ignore_events:
2242 return
2243 list_no_log_for = gajim.config.get_per('accounts', self.current_account,
2244 'no_log_for').split()
2245 if self.current_account in list_no_log_for:
2246 list_no_log_for.remove(self.current_account)
2248 if not widget.get_active():
2249 list_no_log_for.append(self.current_account)
2250 gajim.config.set_per('accounts', self.current_account, 'no_log_for',
2251 ' '.join(list_no_log_for))
2253 def on_sync_with_global_status_checkbutton_toggled(self, widget):
2254 if self.ignore_events:
2255 return
2256 self.on_checkbutton_toggled(widget, 'sync_with_global_status',
2257 account=self.current_account)
2258 gajim.interface.roster.update_status_combobox()
2260 def on_use_ft_proxies_checkbutton1_toggled(self, widget):
2261 if self.ignore_events:
2262 return
2263 self.on_checkbutton_toggled(widget, 'use_ft_proxies',
2264 account=self.current_account)
2266 def on_use_env_http_proxy_checkbutton1_toggled(self, widget):
2267 if self.ignore_events:
2268 return
2269 self.on_checkbutton_toggled(widget, 'use_env_http_proxy',
2270 account=self.current_account)
2271 hbox = self.xml.get_object('proxy_hbox1')
2272 hbox.set_sensitive(not widget.get_active())
2274 def on_proxies_combobox1_changed(self, widget):
2275 active = widget.get_active()
2276 proxy = widget.get_model()[active][0].decode('utf-8')
2277 if proxy == _('None'):
2278 proxy = ''
2280 if self.option_changed('proxy', proxy):
2281 self.need_relogin = True
2283 gajim.config.set_per('accounts', self.current_account, 'proxy', proxy)
2285 def on_manage_proxies_button1_clicked(self, widget):
2286 if 'manage_proxies' in gajim.interface.instances:
2287 gajim.interface.instances['manage_proxies'].window.present()
2288 else:
2289 gajim.interface.instances['manage_proxies'] = ManageProxiesWindow()
2291 def on_warn_when_insecure_connection_checkbutton1_toggled(self, widget):
2292 if self.ignore_events:
2293 return
2295 self.on_checkbutton_toggled(widget, 'warn_when_insecure_ssl_connection',
2296 account=self.current_account)
2298 def on_send_keepalive_checkbutton1_toggled(self, widget):
2299 if self.ignore_events:
2300 return
2301 self.on_checkbutton_toggled(widget, 'keep_alives_enabled',
2302 account=self.current_account)
2303 gajim.config.set_per('accounts', self.current_account,
2304 'ping_alives_enabled', widget.get_active())
2306 def on_custom_host_port_checkbutton1_toggled(self, widget):
2307 if self.option_changed('use_custom_host', widget.get_active()):
2308 self.need_relogin = True
2310 self.on_checkbutton_toggled(widget, 'use_custom_host',
2311 account=self.current_account)
2312 active = widget.get_active()
2313 self.xml.get_object('custom_host_port_hbox1').set_sensitive(active)
2315 def on_custom_host_entry1_changed(self, widget):
2316 if self.ignore_events:
2317 return
2318 host = widget.get_text().decode('utf-8')
2319 if self.option_changed('custom_host', host):
2320 self.need_relogin = True
2321 gajim.config.set_per('accounts', self.current_account, 'custom_host',
2322 host)
2324 def on_custom_port_entry_focus_out_event(self, widget, event):
2325 if self.ignore_events:
2326 return
2327 custom_port = widget.get_text()
2328 try:
2329 custom_port = int(custom_port)
2330 except Exception:
2331 if not widget.is_focus():
2332 dialogs.ErrorDialog(_('Invalid entry'),
2333 _('Custom port must be a port number.'))
2334 gobject.idle_add(lambda: widget.grab_focus())
2335 return True
2336 if self.option_changed('custom_port', custom_port):
2337 self.need_relogin = True
2338 gajim.config.set_per('accounts', self.current_account, 'custom_port',
2339 custom_port)
2341 def on_gpg_choose_button_clicked(self, widget, data = None):
2342 if self.current_account in gajim.connections and \
2343 gajim.connections[self.current_account].gpg:
2344 secret_keys = gajim.connections[self.current_account].\
2345 ask_gpg_secrete_keys()
2347 # self.current_account is None and/or gajim.connections is {}
2348 else:
2349 if gajim.HAVE_GPG:
2350 secret_keys = gpg.GnuPG().get_secret_keys()
2351 else:
2352 secret_keys = []
2353 if not secret_keys:
2354 dialogs.ErrorDialog(_('Failed to get secret keys'),
2355 _('There is no OpenPGP secret key available.'))
2356 secret_keys[_('None')] = _('None')
2358 def on_key_selected(keyID):
2359 if keyID is None:
2360 return
2361 if self.current_account == gajim.ZEROCONF_ACC_NAME:
2362 wiget_name_ext = '2'
2363 else:
2364 wiget_name_ext = '1'
2365 gpg_key_label = self.xml.get_object('gpg_key_label' + wiget_name_ext)
2366 gpg_name_label = self.xml.get_object('gpg_name_label' + wiget_name_ext)
2367 use_gpg_agent_checkbutton = self.xml.get_object(
2368 'use_gpg_agent_checkbutton' + wiget_name_ext)
2369 if keyID[0] == _('None'):
2370 gpg_key_label.set_text(_('No key selected'))
2371 gpg_name_label.set_text('')
2372 use_gpg_agent_checkbutton.set_sensitive(False)
2373 if self.option_changed('keyid', ''):
2374 self.need_relogin = True
2375 gajim.config.set_per('accounts', self.current_account, 'keyname',
2377 gajim.config.set_per('accounts', self.current_account, 'keyid', '')
2378 else:
2379 gpg_key_label.set_text(keyID[0])
2380 gpg_name_label.set_text(keyID[1])
2381 use_gpg_agent_checkbutton.set_sensitive(True)
2382 if self.option_changed('keyid', keyID[0]):
2383 self.need_relogin = True
2384 gajim.config.set_per('accounts', self.current_account, 'keyname',
2385 keyID[1])
2386 gajim.config.set_per('accounts', self.current_account, 'keyid',
2387 keyID[0])
2389 dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
2390 _('Choose your OpenPGP key'), secret_keys, on_key_selected)
2392 def on_use_gpg_agent_checkbutton_toggled(self, widget):
2393 self.on_checkbutton_toggled(widget, 'use_gpg_agent')
2395 def on_edit_details_button1_clicked(self, widget):
2396 if self.current_account not in gajim.interface.instances:
2397 dialogs.ErrorDialog(_('No such account available'),
2398 _('You must create your account before editing your personal '
2399 'information.'))
2400 return
2402 # show error dialog if account is newly created (not in gajim.connections)
2403 if self.current_account not in gajim.connections or \
2404 gajim.connections[self.current_account].connected < 2:
2405 dialogs.ErrorDialog(_('You are not connected to the server'),
2406 _('Without a connection, you can not edit your personal information.'))
2407 return
2409 if not gajim.connections[self.current_account].vcard_supported:
2410 dialogs.ErrorDialog(_("Your server doesn't support Vcard"),
2411 _("Your server can't save your personal information."))
2412 return
2414 gajim.interface.edit_own_details(self.current_account)
2416 def on_checkbutton_toggled(self, widget, config_name,
2417 change_sensitivity_widgets = None, account = None):
2418 if account:
2419 gajim.config.set_per('accounts', account, config_name,
2420 widget.get_active())
2421 else:
2422 gajim.config.set(config_name, widget.get_active())
2423 if change_sensitivity_widgets:
2424 for w in change_sensitivity_widgets:
2425 w.set_sensitive(widget.get_active())
2426 gajim.interface.save_config()
2428 def on_merge_checkbutton_toggled(self, widget):
2429 self.on_checkbutton_toggled(widget, 'mergeaccounts')
2430 if len(gajim.connections) >= 2: # Do not merge accounts if only one active
2431 gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
2432 else:
2433 gajim.interface.roster.regroup = False
2434 gajim.interface.roster.setup_and_draw_roster()
2436 def _disable_account(self, account):
2437 gajim.interface.roster.close_all(account)
2438 if account == gajim.ZEROCONF_ACC_NAME:
2439 gajim.connections[account].disable_account()
2440 del gajim.connections[account]
2441 gajim.interface.save_config()
2442 del gajim.interface.instances[account]
2443 del gajim.interface.minimized_controls[account]
2444 del gajim.nicks[account]
2445 del gajim.block_signed_in_notifications[account]
2446 del gajim.groups[account]
2447 gajim.contacts.remove_account(account)
2448 del gajim.gc_connected[account]
2449 del gajim.automatic_rooms[account]
2450 del gajim.to_be_removed[account]
2451 del gajim.newly_added[account]
2452 del gajim.sleeper_state[account]
2453 del gajim.encrypted_chats[account]
2454 del gajim.last_message_time[account]
2455 del gajim.status_before_autoaway[account]
2456 del gajim.transport_avatar[account]
2457 del gajim.gajim_optional_features[account]
2458 del gajim.caps_hash[account]
2459 if len(gajim.connections) >= 2:
2460 # Do not merge accounts if only one exists
2461 gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
2462 else:
2463 gajim.interface.roster.regroup = False
2464 gajim.interface.roster.setup_and_draw_roster()
2465 gajim.interface.roster.set_actions_menu_needs_rebuild()
2467 def _enable_account(self, account):
2468 if account == gajim.ZEROCONF_ACC_NAME:
2469 gajim.connections[account] = connection_zeroconf.ConnectionZeroconf(
2470 account)
2471 if gajim.connections[account].gpg:
2472 self.xml.get_object('gpg_choose_button2').set_sensitive(True)
2473 else:
2474 gajim.connections[account] = common.connection.Connection(account)
2475 if gajim.connections[account].gpg:
2476 self.xml.get_object('gpg_choose_button1').set_sensitive(True)
2477 self.init_account_gpg()
2478 # update variables
2479 gajim.interface.instances[account] = {'infos': {},
2480 'disco': {}, 'gc_config': {}, 'search': {}, 'online_dialog': {}}
2481 gajim.interface.minimized_controls[account] = {}
2482 gajim.connections[account].connected = 0
2483 gajim.groups[account] = {}
2484 gajim.contacts.add_account(account)
2485 gajim.gc_connected[account] = {}
2486 gajim.automatic_rooms[account] = {}
2487 gajim.newly_added[account] = []
2488 gajim.to_be_removed[account] = []
2489 if account == gajim.ZEROCONF_ACC_NAME:
2490 gajim.nicks[account] = gajim.ZEROCONF_ACC_NAME
2491 else:
2492 gajim.nicks[account] = gajim.config.get_per('accounts', account,
2493 'name')
2494 gajim.block_signed_in_notifications[account] = True
2495 gajim.sleeper_state[account] = 'off'
2496 gajim.encrypted_chats[account] = []
2497 gajim.last_message_time[account] = {}
2498 gajim.status_before_autoaway[account] = ''
2499 gajim.transport_avatar[account] = {}
2500 gajim.gajim_optional_features[account] = []
2501 gajim.caps_hash[account] = ''
2502 # refresh roster
2503 if len(gajim.connections) >= 2:
2504 # Do not merge accounts if only one exists
2505 gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
2506 else:
2507 gajim.interface.roster.regroup = False
2508 gajim.interface.roster.setup_and_draw_roster()
2509 gajim.interface.roster.set_actions_menu_needs_rebuild()
2510 gajim.interface.save_config()
2512 def on_enable_zeroconf_checkbutton2_toggled(self, widget):
2513 # don't do anything if there is an account with the local name but is a
2514 # normal account
2515 if self.ignore_events:
2516 return
2517 if self.current_account in gajim.connections and \
2518 gajim.connections[self.current_account].connected > 0:
2519 self.ignore_events = True
2520 self.xml.get_object('enable_zeroconf_checkbutton2').set_active(True)
2521 self.ignore_events = False
2522 dialogs.ErrorDialog(
2523 _('You are currently connected to the server'),
2524 _('To disable the account, you must be disconnected.'))
2525 return
2526 if gajim.ZEROCONF_ACC_NAME in gajim.connections and not \
2527 gajim.connections[gajim.ZEROCONF_ACC_NAME].is_zeroconf:
2528 gajim.connections[gajim.ZEROCONF_ACC_NAME].dispatch('ERROR',
2529 (_('Account Local already exists.'),
2530 _('Please rename or remove it before enabling link-local messaging'
2531 '.')))
2532 return
2534 if gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'active') \
2535 and not widget.get_active():
2536 self.xml.get_object('zeroconf_notebook').set_sensitive(False)
2537 # disable
2538 self._disable_account(gajim.ZEROCONF_ACC_NAME)
2540 elif not gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,
2541 'active') and widget.get_active():
2542 self.xml.get_object('zeroconf_notebook').set_sensitive(True)
2543 # enable (will create new account if not present)
2544 self._enable_account(gajim.ZEROCONF_ACC_NAME)
2546 self.on_checkbutton_toggled(widget, 'active',
2547 account=gajim.ZEROCONF_ACC_NAME)
2549 def on_enable_checkbutton1_toggled(self, widget):
2550 if self.ignore_events:
2551 return
2552 if self.current_account in gajim.connections and \
2553 gajim.connections[self.current_account].connected > 0:
2554 # connecting or connected
2555 self.ignore_events = True
2556 self.xml.get_object('enable_checkbutton1').set_active(True)
2557 self.ignore_events = False
2558 dialogs.ErrorDialog(
2559 _('You are currently connected to the server'),
2560 _('To disable the account, you must be disconnected.'))
2561 return
2562 # add/remove account in roster and all variables
2563 if widget.get_active():
2564 # enable
2565 self._enable_account(self.current_account)
2566 else:
2567 # disable
2568 self._disable_account(self.current_account)
2569 self.on_checkbutton_toggled(widget, 'active',
2570 account=self.current_account, change_sensitivity_widgets=[
2571 self.xml.get_object('normal_notebook1')])
2573 def on_custom_port_checkbutton2_toggled(self, widget):
2574 self.xml.get_object('custom_port_entry2').set_sensitive(
2575 widget.get_active())
2576 self.on_checkbutton_toggled(widget, 'use_custom_host',
2577 account = self.current_account)
2578 if not widget.get_active():
2579 self.xml.get_object('custom_port_entry2').set_text('5298')
2581 def on_first_name_entry2_changed(self, widget):
2582 if self.ignore_events:
2583 return
2584 name = widget.get_text().decode('utf-8')
2585 if self.option_changed('zeroconf_first_name', name):
2586 self.need_relogin = True
2587 gajim.config.set_per('accounts', self.current_account,
2588 'zeroconf_first_name', name)
2590 def on_last_name_entry2_changed(self, widget):
2591 if self.ignore_events:
2592 return
2593 name = widget.get_text().decode('utf-8')
2594 if self.option_changed('zeroconf_last_name', name):
2595 self.need_relogin = True
2596 gajim.config.set_per('accounts', self.current_account,
2597 'zeroconf_last_name', name)
2599 def on_jabber_id_entry2_changed(self, widget):
2600 if self.ignore_events:
2601 return
2602 id_ = widget.get_text().decode('utf-8')
2603 if self.option_changed('zeroconf_jabber_id', id_):
2604 self.need_relogin = True
2605 gajim.config.set_per('accounts', self.current_account,
2606 'zeroconf_jabber_id', id_)
2608 def on_email_entry2_changed(self, widget):
2609 if self.ignore_events:
2610 return
2611 email = widget.get_text().decode('utf-8')
2612 if self.option_changed('zeroconf_email', email):
2613 self.need_relogin = True
2614 gajim.config.set_per('accounts', self.current_account,
2615 'zeroconf_email', email)
2617 class FakeDataForm(gtk.Table, object):
2619 Class for forms that are in XML format <entry1>value1</entry1> infos in a
2620 table {entry1: value1}
2623 def __init__(self, infos):
2624 gtk.Table.__init__(self)
2625 self.infos = infos
2626 self.entries = {}
2627 self._draw_table()
2629 def _draw_table(self):
2631 Draw the table
2633 nbrow = 0
2634 if 'instructions' in self.infos:
2635 nbrow = 1
2636 self.resize(rows = nbrow, columns = 2)
2637 label = gtk.Label(self.infos['instructions'])
2638 self.attach(label, 0, 2, 0, 1, 0, 0, 0, 0)
2639 for name in self.infos.keys():
2640 if name in ('key', 'instructions', 'x', 'registered'):
2641 continue
2642 if not name:
2643 continue
2645 nbrow = nbrow + 1
2646 self.resize(rows = nbrow, columns = 2)
2647 label = gtk.Label(name.capitalize() + ':')
2648 self.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0)
2649 entry = gtk.Entry()
2650 entry.set_activates_default(True)
2651 if self.infos[name]:
2652 entry.set_text(self.infos[name])
2653 if name == 'password':
2654 entry.set_visibility(False)
2655 self.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0)
2656 self.entries[name] = entry
2657 if nbrow == 1:
2658 entry.grab_focus()
2660 def get_infos(self):
2661 for name in self.entries.keys():
2662 self.infos[name] = self.entries[name].get_text().decode('utf-8')
2663 return self.infos
2665 class ServiceRegistrationWindow:
2667 Class for Service registration window. Window that appears when we want to
2668 subscribe to a service if is_form we use dataforms_widget else we use
2669 service_registarion_window
2671 def __init__(self, service, infos, account, is_form):
2672 self.service = service
2673 self.account = account
2674 self.is_form = is_form
2675 self.xml = gtkgui_helpers.get_gtk_builder('service_registration_window.ui')
2676 self.window = self.xml.get_object('service_registration_window')
2677 self.window.set_transient_for(gajim.interface.roster.window)
2678 if self.is_form:
2679 dataform = dataforms.ExtendForm(node = infos)
2680 self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
2681 if self.data_form_widget.title:
2682 self.window.set_title('%s - Gajim' % self.data_form_widget.title)
2683 table = self.xml.get_object('table')
2684 table.attach(self.data_form_widget, 0, 2, 0, 1)
2685 else:
2686 if 'registered' in infos:
2687 self.window.set_title(_('Edit %s') % service)
2688 else:
2689 self.window.set_title(_('Register to %s') % service)
2690 self.data_form_widget = FakeDataForm(infos)
2691 table = self.xml.get_object('table')
2692 table.attach(self.data_form_widget, 0, 2, 0, 1)
2694 self.xml.connect_signals(self)
2695 self.window.show_all()
2697 def on_cancel_button_clicked(self, widget):
2698 self.window.destroy()
2700 def on_ok_button_clicked(self, widget):
2701 # send registration info to the core
2702 if self.is_form:
2703 form = self.data_form_widget.data_form
2704 gajim.connections[self.account].register_agent(self.service,
2705 form, True) # True is for is_form
2706 else:
2707 infos = self.data_form_widget.get_infos()
2708 if 'instructions' in infos:
2709 del infos['instructions']
2710 if 'registered' in infos:
2711 del infos['registered']
2712 gajim.connections[self.account].register_agent(self.service, infos)
2714 self.window.destroy()
2716 class GroupchatConfigWindow:
2718 def __init__(self, account, room_jid, form=None):
2719 self.account = account
2720 self.room_jid = room_jid
2721 self.form = form
2722 self.remove_button = {}
2723 self.affiliation_treeview = {}
2724 self.start_users_dict = {} # list at the beginning
2725 self.affiliation_labels = {'outcast': _('Ban List'),
2726 'member': _('Member List'), 'owner': _('Owner List'),
2727 'admin':_('Administrator List')}
2729 self.xml = gtkgui_helpers.get_gtk_builder('data_form_window.ui',
2730 'data_form_window')
2731 self.window = self.xml.get_object('data_form_window')
2732 self.window.set_transient_for(gajim.interface.roster.window)
2734 if self.form:
2735 config_vbox = self.xml.get_object('config_vbox')
2736 self.data_form_widget = dataforms_widget.DataFormWidget(self.form)
2737 # hide scrollbar of this data_form_widget, we already have in this
2738 # widget
2739 sw = self.data_form_widget.xml.get_object(
2740 'single_form_scrolledwindow')
2741 sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
2742 if self.form.title:
2743 self.xml.get_object('title_label').set_text(self.form.title)
2744 else:
2745 self.xml.get_object('title_hseparator').set_no_show_all(True)
2746 self.xml.get_object('title_hseparator').hide()
2748 self.data_form_widget.show()
2749 config_vbox.pack_start(self.data_form_widget)
2750 else:
2751 self.xml.get_object('title_label').set_no_show_all(True)
2752 self.xml.get_object('title_label').hide()
2753 self.xml.get_object('title_hseparator').set_no_show_all(True)
2754 self.xml.get_object('title_hseparator').hide()
2755 self.xml.get_object('config_hseparator').set_no_show_all(True)
2756 self.xml.get_object('config_hseparator').hide()
2758 # Draw the edit affiliation list things
2759 add_on_vbox = self.xml.get_object('add_on_vbox')
2761 for affiliation in self.affiliation_labels.keys():
2762 self.start_users_dict[affiliation] = {}
2763 hbox = gtk.HBox(spacing=5)
2764 add_on_vbox.pack_start(hbox, False)
2766 label = gtk.Label(self.affiliation_labels[affiliation])
2767 hbox.pack_start(label, False)
2769 bb = gtk.HButtonBox()
2770 bb.set_layout(gtk.BUTTONBOX_END)
2771 bb.set_spacing(5)
2772 hbox.pack_start(bb)
2773 add_button = gtk.Button(stock=gtk.STOCK_ADD)
2774 add_button.connect('clicked', self.on_add_button_clicked,
2775 affiliation)
2776 bb.pack_start(add_button)
2777 self.remove_button[affiliation] = gtk.Button(stock=gtk.STOCK_REMOVE)
2778 self.remove_button[affiliation].set_sensitive(False)
2779 self.remove_button[affiliation].connect('clicked',
2780 self.on_remove_button_clicked, affiliation)
2781 bb.pack_start(self.remove_button[affiliation])
2783 # jid, reason, nick, role
2784 liststore = gtk.ListStore(str, str, str, str)
2785 self.affiliation_treeview[affiliation] = gtk.TreeView(liststore)
2786 self.affiliation_treeview[affiliation].get_selection().set_mode(
2787 gtk.SELECTION_MULTIPLE)
2788 self.affiliation_treeview[affiliation].connect('cursor-changed',
2789 self.on_affiliation_treeview_cursor_changed, affiliation)
2790 renderer = gtk.CellRendererText()
2791 col = gtk.TreeViewColumn(_('JID'), renderer)
2792 col.add_attribute(renderer, 'text', 0)
2793 col.set_resizable(True)
2794 col.set_sort_column_id(0)
2795 self.affiliation_treeview[affiliation].append_column(col)
2797 if affiliation == 'outcast':
2798 renderer = gtk.CellRendererText()
2799 renderer.set_property('editable', True)
2800 renderer.connect('edited', self.on_cell_edited)
2801 col = gtk.TreeViewColumn(_('Reason'), renderer)
2802 col.add_attribute(renderer, 'text', 1)
2803 col.set_resizable(True)
2804 col.set_sort_column_id(1)
2805 self.affiliation_treeview[affiliation].append_column(col)
2806 elif affiliation == 'member':
2807 renderer = gtk.CellRendererText()
2808 col = gtk.TreeViewColumn(_('Nick'), renderer)
2809 col.add_attribute(renderer, 'text', 2)
2810 col.set_resizable(True)
2811 col.set_sort_column_id(2)
2812 self.affiliation_treeview[affiliation].append_column(col)
2813 renderer = gtk.CellRendererText()
2814 col = gtk.TreeViewColumn(_('Role'), renderer)
2815 col.add_attribute(renderer, 'text', 3)
2816 col.set_resizable(True)
2817 col.set_sort_column_id(3)
2818 self.affiliation_treeview[affiliation].append_column(col)
2820 sw = gtk.ScrolledWindow()
2821 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
2822 sw.add(self.affiliation_treeview[affiliation])
2823 add_on_vbox.pack_start(sw)
2824 gajim.connections[self.account].get_affiliation_list(self.room_jid,
2825 affiliation)
2827 self.xml.connect_signals(self)
2828 self.window.show_all()
2830 def on_cancel_button_clicked(self, widget):
2831 self.window.destroy()
2833 def on_cell_edited(self, cell, path, new_text):
2834 model = self.affiliation_treeview['outcast'].get_model()
2835 new_text = new_text.decode('utf-8')
2836 iter_ = model.get_iter(path)
2837 model[iter_][1] = new_text
2839 def on_add_button_clicked(self, widget, affiliation):
2840 if affiliation == 'outcast':
2841 title = _('Banning...')
2842 #You can move '\n' before user@domain if that line is TOO BIG
2843 prompt = _('<b>Whom do you want to ban?</b>\n\n')
2844 elif affiliation == 'member':
2845 title = _('Adding Member...')
2846 prompt = _('<b>Whom do you want to make a member?</b>\n\n')
2847 elif affiliation == 'owner':
2848 title = _('Adding Owner...')
2849 prompt = _('<b>Whom do you want to make an owner?</b>\n\n')
2850 else:
2851 title = _('Adding Administrator...')
2852 prompt = _('<b>Whom do you want to make an administrator?</b>\n\n')
2853 prompt += _('Can be one of the following:\n'
2854 '1. user@domain/resource (only that resource matches).\n'
2855 '2. user@domain (any resource matches).\n'
2856 '3. domain/resource (only that resource matches).\n'
2857 '4. domain (the domain itself matches, as does any user@domain,\n'
2858 'domain/resource, or address containing a subdomain).')
2860 def on_ok(jid):
2861 if not jid:
2862 return
2863 model = self.affiliation_treeview[affiliation].get_model()
2864 model.append((jid, '', '', ''))
2865 dialogs.InputDialog(title, prompt, ok_handler=on_ok)
2867 def on_remove_button_clicked(self, widget, affiliation):
2868 selection = self.affiliation_treeview[affiliation].get_selection()
2869 model, paths = selection.get_selected_rows()
2870 row_refs = []
2871 for path in paths:
2872 row_refs.append(gtk.TreeRowReference(model, path))
2873 for row_ref in row_refs:
2874 path = row_ref.get_path()
2875 iter_ = model.get_iter(path)
2876 model.remove(iter_)
2877 self.remove_button[affiliation].set_sensitive(False)
2879 def on_affiliation_treeview_cursor_changed(self, widget, affiliation):
2880 self.remove_button[affiliation].set_sensitive(True)
2882 def affiliation_list_received(self, users_dict):
2884 Fill the affiliation treeview
2886 for jid in users_dict:
2887 affiliation = users_dict[jid]['affiliation']
2888 if affiliation not in self.affiliation_labels.keys():
2889 # Unknown affiliation or 'none' affiliation, do not show it
2890 continue
2891 self.start_users_dict[affiliation][jid] = users_dict[jid]
2892 tv = self.affiliation_treeview[affiliation]
2893 model = tv.get_model()
2894 reason = users_dict[jid].get('reason', '')
2895 nick = users_dict[jid].get('nick', '')
2896 role = users_dict[jid].get('role', '')
2897 model.append((jid, reason, nick, role))
2899 def on_data_form_window_destroy(self, widget):
2900 del gajim.interface.instances[self.account]['gc_config'][self.room_jid]
2902 def on_ok_button_clicked(self, widget):
2903 if self.form:
2904 form = self.data_form_widget.data_form
2905 gajim.connections[self.account].send_gc_config(self.room_jid, form)
2906 for affiliation in self.affiliation_labels.keys():
2907 users_dict = {}
2908 actual_jid_list = []
2909 model = self.affiliation_treeview[affiliation].get_model()
2910 iter_ = model.get_iter_first()
2911 # add new jid
2912 while iter_:
2913 jid = model[iter_][0].decode('utf-8')
2914 actual_jid_list.append(jid)
2915 if jid not in self.start_users_dict[affiliation] or \
2916 (affiliation == 'outcast' and 'reason' in self.start_users_dict[
2917 affiliation][jid] and self.start_users_dict[affiliation][jid]\
2918 ['reason'] != model[iter_][1].decode('utf-8')):
2919 users_dict[jid] = {'affiliation': affiliation}
2920 if affiliation == 'outcast':
2921 users_dict[jid]['reason'] = model[iter_][1].decode(
2922 'utf-8')
2923 iter_ = model.iter_next(iter_)
2924 # remove removed one
2925 for jid in self.start_users_dict[affiliation]:
2926 if jid not in actual_jid_list:
2927 users_dict[jid] = {'affiliation': 'none'}
2928 if users_dict:
2929 gajim.connections[self.account].send_gc_affiliation_list(
2930 self.room_jid, users_dict)
2931 self.window.destroy()
2933 #---------- RemoveAccountWindow class -------------#
2934 class RemoveAccountWindow:
2936 Ask for removing from gajim only or from gajim and server too and do
2937 removing of the account given
2940 def on_remove_account_window_destroy(self, widget):
2941 if self.account in gajim.interface.instances:
2942 del gajim.interface.instances[self.account]['remove_account']
2944 def on_cancel_button_clicked(self, widget):
2945 self.window.destroy()
2947 def __init__(self, account):
2948 self.account = account
2949 xml = gtkgui_helpers.get_gtk_builder('remove_account_window.ui')
2950 self.window = xml.get_object('remove_account_window')
2951 self.window.set_transient_for(gajim.interface.roster.window)
2952 self.remove_and_unregister_radiobutton = xml.get_object(
2953 'remove_and_unregister_radiobutton')
2954 self.window.set_title(_('Removing %s account') % self.account)
2955 xml.connect_signals(self)
2956 self.window.show_all()
2958 def on_remove_button_clicked(self, widget):
2959 def remove():
2960 if self.account in gajim.connections and \
2961 gajim.connections[self.account].connected and \
2962 not self.remove_and_unregister_radiobutton.get_active():
2963 # change status to offline only if we will not remove this JID from
2964 # server
2965 gajim.connections[self.account].change_status('offline', 'offline')
2966 if self.remove_and_unregister_radiobutton.get_active():
2967 if not self.account in gajim.connections:
2968 dialogs.ErrorDialog(
2969 _('Account is disabled'),
2970 _('To unregister from a server, account must be '
2971 'enabled.'))
2972 return
2973 if not gajim.connections[self.account].password:
2974 def on_ok(passphrase, checked):
2975 if passphrase == -1:
2976 # We don't remove account cause we canceled pw window
2977 return
2978 gajim.connections[self.account].password = passphrase
2979 gajim.connections[self.account].unregister_account(
2980 self._on_remove_success)
2982 dialogs.PassphraseDialog(
2983 _('Password Required'),
2984 _('Enter your password for account %s') % self.account,
2985 _('Save password'), ok_handler=on_ok)
2986 return
2987 gajim.connections[self.account].unregister_account(
2988 self._on_remove_success)
2989 else:
2990 self._on_remove_success(True)
2992 if self.account in gajim.connections and \
2993 gajim.connections[self.account].connected:
2994 dialogs.ConfirmationDialog(
2995 _('Account "%s" is connected to the server') % self.account,
2996 _('If you remove it, the connection will be lost.'),
2997 on_response_ok=remove)
2998 else:
2999 remove()
3001 def on_remove_responce_ok(self, is_checked):
3002 if is_checked[0]:
3003 self._on_remove_success(True)
3005 def _on_remove_success(self, res):
3006 # action of unregistration has failed, we don't remove the account
3007 # Error message is send by connect_and_auth()
3008 if not res:
3009 dialogs.ConfirmationDialogDoubleRadio(
3010 _('Connection to server %s failed') % self.account,
3011 _('What would you like to do?'),
3012 _('Remove only from Gajim'),
3013 _('Don\'t remove anything. I\'ll try again later'),
3014 on_response_ok=self.on_remove_responce_ok, is_modal=False)
3015 return
3016 # Close all opened windows
3017 gajim.interface.roster.close_all(self.account, force=True)
3018 if self.account in gajim.connections:
3019 gajim.connections[self.account].disconnect(on_purpose=True)
3020 gajim.connections[self.account].cleanup()
3021 del gajim.connections[self.account]
3022 gajim.logger.remove_roster(gajim.get_jid_from_account(self.account))
3023 gajim.config.del_per('accounts', self.account)
3024 gajim.interface.save_config()
3025 del gajim.interface.instances[self.account]
3026 if self.account in gajim.nicks:
3027 del gajim.interface.minimized_controls[self.account]
3028 del gajim.nicks[self.account]
3029 del gajim.block_signed_in_notifications[self.account]
3030 del gajim.groups[self.account]
3031 gajim.contacts.remove_account(self.account)
3032 del gajim.gc_connected[self.account]
3033 del gajim.automatic_rooms[self.account]
3034 del gajim.to_be_removed[self.account]
3035 del gajim.newly_added[self.account]
3036 del gajim.sleeper_state[self.account]
3037 del gajim.encrypted_chats[self.account]
3038 del gajim.last_message_time[self.account]
3039 del gajim.status_before_autoaway[self.account]
3040 del gajim.transport_avatar[self.account]
3041 del gajim.gajim_optional_features[self.account]
3042 del gajim.caps_hash[self.account]
3043 if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
3044 gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
3045 else:
3046 gajim.interface.roster.regroup = False
3047 gajim.interface.roster.setup_and_draw_roster()
3048 gajim.interface.roster.set_actions_menu_needs_rebuild()
3049 if 'accounts' in gajim.interface.instances:
3050 gajim.interface.instances['accounts'].init_accounts()
3051 gajim.interface.instances['accounts'].init_account()
3052 self.window.destroy()
3054 #---------- ManageBookmarksWindow class -------------#
3055 class ManageBookmarksWindow:
3056 def __init__(self):
3057 self.xml = gtkgui_helpers.get_gtk_builder('manage_bookmarks_window.ui')
3058 self.window = self.xml.get_object('manage_bookmarks_window')
3059 self.window.set_transient_for(gajim.interface.roster.window)
3061 # Account-JID, RoomName, Room-JID, Autojoin, Minimize, Passowrd, Nick,
3062 # Show_Status
3063 self.treestore = gtk.TreeStore(str, str, str, bool, bool, str, str, str)
3064 self.treestore.set_sort_column_id(1, gtk.SORT_ASCENDING)
3066 # Store bookmarks in treeview.
3067 for account in gajim.connections:
3068 if gajim.connections[account].connected <= 1:
3069 continue
3070 if gajim.connections[account].is_zeroconf:
3071 continue
3072 if not gajim.connections[account].private_storage_supported:
3073 continue
3074 iter_ = self.treestore.append(None, [None, account, None, None,
3075 None, None, None, None])
3077 for bookmark in gajim.connections[account].bookmarks:
3078 if bookmark['name'] == '':
3079 # No name was given for this bookmark.
3080 # Use the first part of JID instead...
3081 name = bookmark['jid'].split("@")[0]
3082 bookmark['name'] = name
3084 # make '1', '0', 'true', 'false' (or other) to True/False
3085 autojoin = helpers.from_xs_boolean_to_python_boolean(
3086 bookmark['autojoin'])
3088 minimize = helpers.from_xs_boolean_to_python_boolean(
3089 bookmark['minimize'])
3091 print_status = bookmark.get('print_status', '')
3092 if print_status not in ('', 'all', 'in_and_out', 'none'):
3093 print_status = ''
3094 self.treestore.append(iter_, [
3095 account,
3096 bookmark['name'],
3097 bookmark['jid'],
3098 autojoin,
3099 minimize,
3100 bookmark['password'],
3101 bookmark['nick'],
3102 print_status ])
3104 self.print_status_combobox = self.xml.get_object('print_status_combobox')
3105 model = gtk.ListStore(str, str)
3107 self.option_list = {'': _('Default'), 'all': Q_('?print_status:All'),
3108 'in_and_out': _('Enter and leave only'),
3109 'none': Q_('?print_status:None')}
3110 opts = sorted(self.option_list.keys())
3111 for opt in opts:
3112 model.append([self.option_list[opt], opt])
3114 self.print_status_combobox.set_model(model)
3115 self.print_status_combobox.set_active(1)
3117 self.view = self.xml.get_object('bookmarks_treeview')
3118 self.view.set_model(self.treestore)
3119 self.view.expand_all()
3121 renderer = gtk.CellRendererText()
3122 column = gtk.TreeViewColumn('Bookmarks', renderer, text=1)
3123 self.view.append_column(column)
3125 self.selection = self.view.get_selection()
3126 self.selection.connect('changed', self.bookmark_selected)
3128 #Prepare input fields
3129 self.title_entry = self.xml.get_object('title_entry')
3130 self.title_entry.connect('changed', self.on_title_entry_changed)
3131 self.nick_entry = self.xml.get_object('nick_entry')
3132 self.nick_entry.connect('changed', self.on_nick_entry_changed)
3133 self.server_entry = self.xml.get_object('server_entry')
3134 self.server_entry.connect('changed', self.on_server_entry_changed)
3135 self.room_entry = self.xml.get_object('room_entry')
3136 self.room_entry.connect('changed', self.on_room_entry_changed)
3137 self.pass_entry = self.xml.get_object('pass_entry')
3138 self.pass_entry.connect('changed', self.on_pass_entry_changed)
3139 self.autojoin_checkbutton = self.xml.get_object('autojoin_checkbutton')
3140 self.minimize_checkbutton = self.xml.get_object('minimize_checkbutton')
3142 self.xml.connect_signals(self)
3143 self.window.show_all()
3144 # select root iter
3145 self.selection.select_iter(self.treestore.get_iter_root())
3147 def on_bookmarks_treeview_button_press_event(self, widget, event):
3148 (model, iter_) = self.selection.get_selected()
3149 if not iter_:
3150 # Removed a bookmark before
3151 return
3153 if model.iter_parent(iter_):
3154 # The currently selected node is a bookmark
3155 return not self.check_valid_bookmark()
3157 def on_manage_bookmarks_window_destroy(self, widget, event):
3158 del gajim.interface.instances['manage_bookmarks']
3160 def on_add_bookmark_button_clicked(self, widget):
3162 Add a new bookmark
3164 # Get the account that is currently used
3165 # (the parent of the currently selected item)
3166 (model, iter_) = self.selection.get_selected()
3167 if not iter_: # Nothing selected, do nothing
3168 return
3170 parent = model.iter_parent(iter_)
3172 if parent:
3173 # We got a bookmark selected, so we add_to the parent
3174 add_to = parent
3175 else:
3176 # No parent, so we got an account -> add to this.
3177 add_to = iter_
3179 account = model[add_to][1].decode('utf-8')
3180 nick = gajim.nicks[account]
3181 iter_ = self.treestore.append(add_to, [account, _('New Group Chat'),
3182 '@', False, False, '', nick, 'in_and_out'])
3184 self.view.expand_row(model.get_path(add_to), True)
3185 self.view.set_cursor(model.get_path(iter_))
3187 def on_remove_bookmark_button_clicked(self, widget):
3189 Remove selected bookmark
3191 (model, iter_) = self.selection.get_selected()
3192 if not iter_: # Nothing selected
3193 return
3195 if not model.iter_parent(iter_):
3196 # Don't remove account iters
3197 return
3199 model.remove(iter_)
3200 self.clear_fields()
3202 def check_valid_bookmark(self):
3204 Check if all neccessary fields are entered correctly
3206 (model, iter_) = self.selection.get_selected()
3208 if not model.iter_parent(iter_):
3209 #Account data can't be changed
3210 return
3212 if self.server_entry.get_text().decode('utf-8') == '' or \
3213 self.room_entry.get_text().decode('utf-8') == '':
3214 dialogs.ErrorDialog(_('This bookmark has invalid data'),
3215 _('Please be sure to fill out server and room fields or remove this'
3216 ' bookmark.'))
3217 return False
3219 return True
3221 def on_ok_button_clicked(self, widget):
3223 Parse the treestore data into our new bookmarks array, then send the new
3224 bookmarks to the server.
3226 (model, iter_) = self.selection.get_selected()
3227 if iter_ and model.iter_parent(iter_):
3228 #bookmark selected, check it
3229 if not self.check_valid_bookmark():
3230 return
3232 for account in self.treestore:
3233 account_unicode = account[1].decode('utf-8')
3234 gajim.connections[account_unicode].bookmarks = []
3236 for bm in account.iterchildren():
3237 # Convert True/False/None to '1' or '0'
3238 autojoin = unicode(int(bm[3]))
3239 minimize = unicode(int(bm[4]))
3240 name = bm[1]
3241 if name:
3242 name = name.decode('utf-8')
3243 jid = bm[2]
3244 if jid:
3245 jid = jid.decode('utf-8')
3246 pw = bm[5]
3247 if pw:
3248 pw = pw.decode('utf-8')
3249 nick = bm[6]
3250 if nick:
3251 nick = nick.decode('utf-8')
3253 # create the bookmark-dict
3254 bmdict = { 'name': name, 'jid': jid, 'autojoin': autojoin,
3255 'minimize': minimize, 'password': pw, 'nick': nick,
3256 'print_status': bm[7]}
3258 gajim.connections[account_unicode].bookmarks.append(bmdict)
3260 gajim.connections[account_unicode].store_bookmarks()
3261 gajim.interface.roster.set_actions_menu_needs_rebuild()
3262 self.window.destroy()
3264 def on_cancel_button_clicked(self, widget):
3265 self.window.destroy()
3267 def bookmark_selected(self, selection):
3269 Fill in the bookmark's data into the fields.
3271 (model, iter_) = selection.get_selected()
3273 if not iter_:
3274 # After removing the last bookmark for one account
3275 # this will be None, so we will just:
3276 return
3278 widgets = [ self.title_entry, self.nick_entry, self.room_entry,
3279 self.server_entry, self.pass_entry, self.autojoin_checkbutton,
3280 self.minimize_checkbutton, self.print_status_combobox]
3282 if model.iter_parent(iter_):
3283 # make the fields sensitive
3284 for field in widgets:
3285 field.set_sensitive(True)
3286 else:
3287 # Top-level has no data (it's the account fields)
3288 # clear fields & make them insensitive
3289 self.clear_fields()
3290 for field in widgets:
3291 field.set_sensitive(False)
3292 return
3294 # Fill in the data for childs
3295 self.title_entry.set_text(model[iter_][1])
3296 room_jid = model[iter_][2].decode('utf-8')
3297 (room, server) = room_jid.split('@')
3298 self.room_entry.set_text(room)
3299 self.server_entry.set_text(server)
3301 self.autojoin_checkbutton.set_active(model[iter_][3])
3302 self.minimize_checkbutton.set_active(model[iter_][4])
3303 # sensitive only if auto join is checked
3304 self.minimize_checkbutton.set_sensitive(model[iter_][3])
3306 if model[iter_][5] is not None:
3307 password = model[iter_][5].decode('utf-8')
3308 else:
3309 password = None
3311 if password:
3312 self.pass_entry.set_text(password)
3313 else:
3314 self.pass_entry.set_text('')
3315 nick = model[iter_][6]
3316 if nick:
3317 nick = nick.decode('utf-8')
3318 self.nick_entry.set_text(nick)
3319 else:
3320 self.nick_entry.set_text('')
3322 print_status = model[iter_][7]
3323 opts = sorted(self.option_list.keys())
3324 self.print_status_combobox.set_active(opts.index(print_status))
3326 def on_title_entry_changed(self, widget):
3327 (model, iter_) = self.selection.get_selected()
3328 if iter_: # After removing a bookmark, we got nothing selected
3329 if model.iter_parent(iter_):
3330 # Don't clear the title field for account nodes
3331 model[iter_][1] = self.title_entry.get_text()
3333 def on_nick_entry_changed(self, widget):
3334 (model, iter_) = self.selection.get_selected()
3335 if iter_:
3336 nick = self.nick_entry.get_text().decode('utf-8')
3337 try:
3338 nick = helpers.parse_resource(nick)
3339 except helpers.InvalidFormat, e:
3340 dialogs.ErrorDialog(_('Invalid nickname'),
3341 _('Character not allowed'))
3342 self.nick_entry.set_text(model[iter_][6])
3343 return True
3344 model[iter_][6] = nick
3346 def on_server_entry_changed(self, widget):
3347 (model, iter_) = self.selection.get_selected()
3348 if not iter_:
3349 return
3350 server = widget.get_text().decode('utf-8')
3351 if '@' in server:
3352 dialogs.ErrorDialog(_('Invalid server'), _('Character not allowed'))
3353 widget.set_text(server.replace('@', ''))
3355 room_jid = self.room_entry.get_text().decode('utf-8').strip() + '@' + \
3356 server.strip()
3357 try:
3358 room_jid = helpers.parse_resource(room_jid)
3359 except helpers.InvalidFormat, e:
3360 dialogs.ErrorDialog(_('Invalid server'),
3361 _('Character not allowed'))
3362 self.server_entry.set_text(model[iter_][2].split('@')[1])
3363 return True
3364 model[iter_][2] = room_jid
3366 def on_room_entry_changed(self, widget):
3367 (model, iter_) = self.selection.get_selected()
3368 if not iter_:
3369 return
3370 room = widget.get_text().decode('utf-8')
3371 if '@' in room:
3372 dialogs.ErrorDialog(_('Invalid server'), _('Character not allowed'))
3373 widget.set_text(room.replace('@', ''))
3374 room_jid = room.strip() + '@' + \
3375 self.server_entry.get_text().decode('utf-8').strip()
3376 try:
3377 room_jid = helpers.parse_resource(room_jid)
3378 except helpers.InvalidFormat, e:
3379 dialogs.ErrorDialog(_('Invalid room'),
3380 _('Character not allowed'))
3381 self.room_entry.set_text(model[iter_][2].split('@')[0])
3382 return True
3383 model[iter_][2] = room_jid
3385 def on_pass_entry_changed(self, widget):
3386 (model, iter_) = self.selection.get_selected()
3387 if iter_:
3388 model[iter_][5] = self.pass_entry.get_text()
3390 def on_autojoin_checkbutton_toggled(self, widget):
3391 (model, iter_) = self.selection.get_selected()
3392 if iter_:
3393 model[iter_][3] = self.autojoin_checkbutton.get_active()
3394 self.minimize_checkbutton.set_sensitive(model[iter_][3])
3396 def on_minimize_checkbutton_toggled(self, widget):
3397 (model, iter_) = self.selection.get_selected()
3398 if iter_:
3399 model[iter_][4] = self.minimize_checkbutton.get_active()
3401 def on_print_status_combobox_changed(self, widget):
3402 active = widget.get_active()
3403 model = widget.get_model()
3404 print_status = model[active][1]
3405 (model2, iter_) = self.selection.get_selected()
3406 if iter_:
3407 model2[iter_][7] = print_status
3409 def clear_fields(self):
3410 widgets = [ self.title_entry, self.nick_entry, self.room_entry,
3411 self.server_entry, self.pass_entry ]
3412 for field in widgets:
3413 field.set_text('')
3414 self.autojoin_checkbutton.set_active(False)
3415 self.minimize_checkbutton.set_active(False)
3416 self.print_status_combobox.set_active(1)
3418 class AccountCreationWizardWindow:
3419 def __init__(self):
3420 self.xml = gtkgui_helpers.get_gtk_builder(
3421 'account_creation_wizard_window.ui')
3422 self.window = self.xml.get_object('account_creation_wizard_window')
3423 self.window.set_transient_for(gajim.interface.roster.window)
3425 completion = gtk.EntryCompletion()
3426 completion1 = gtk.EntryCompletion()
3427 # Connect events from comboboxentry.child
3428 server_comboboxentry = self.xml.get_object('server_comboboxentry')
3429 entry = server_comboboxentry.child
3430 entry.connect('key_press_event',
3431 self.on_server_comboboxentry_key_press_event, server_comboboxentry)
3432 entry.set_completion(completion)
3433 # Do the same for the other server comboboxentry
3434 server_comboboxentry1 = self.xml.get_object('server_comboboxentry1')
3435 entry = server_comboboxentry1.child
3436 entry.set_completion(completion1)
3438 self.update_proxy_list()
3440 # parse servers.xml
3441 servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml')
3442 servers = gtkgui_helpers.parse_server_xml(servers_xml)
3443 servers_model = gtk.ListStore(str)
3444 for server in servers:
3445 servers_model.append((server,))
3447 completion.set_model(servers_model)
3448 completion.set_text_column(0)
3449 completion1.set_model(servers_model)
3450 completion1.set_text_column(0)
3452 # Put servers into comboboxentries
3453 server_comboboxentry.set_model(servers_model)
3454 server_comboboxentry.set_text_column(0)
3455 server_comboboxentry1.set_model(servers_model)
3456 server_comboboxentry1.set_text_column(0)
3458 # Generic widgets
3459 self.notebook = self.xml.get_object('notebook')
3460 self.cancel_button = self.xml.get_object('cancel_button')
3461 self.back_button = self.xml.get_object('back_button')
3462 self.forward_button = self.xml.get_object('forward_button')
3463 self.finish_button = self.xml.get_object('finish_button')
3464 self.advanced_button = self.xml.get_object('advanced_button')
3465 self.finish_label = self.xml.get_object('finish_label')
3466 self.go_online_checkbutton = self.xml.get_object(
3467 'go_online_checkbutton')
3468 self.show_vcard_checkbutton = self.xml.get_object(
3469 'show_vcard_checkbutton')
3470 self.progressbar = self.xml.get_object('progressbar')
3472 # some vars
3473 self.update_progressbar_timeout_id = None
3475 self.notebook.set_current_page(0)
3476 self.xml.connect_signals(self)
3477 self.window.show_all()
3478 gajim.ged.register_event_handler('new-account-connected', ged.GUI1,
3479 self._nec_new_acc_connected)
3480 gajim.ged.register_event_handler('new-account-not-connected', ged.GUI1,
3481 self._nec_new_acc_not_connected)
3482 gajim.ged.register_event_handler('account-created', ged.GUI1,
3483 self._nec_acc_is_ok)
3484 gajim.ged.register_event_handler('account-not-created', ged.GUI1,
3485 self._nec_acc_is_not_ok)
3487 def on_wizard_window_destroy(self, widget):
3488 page = self.notebook.get_current_page()
3489 if page in (4, 5) and self.account in gajim.connections:
3490 # connection instance is saved in gajim.connections and we canceled
3491 # the addition of the account
3492 del gajim.connections[self.account]
3493 if self.account in gajim.config.get_per('accounts'):
3494 gajim.config.del_per('accounts', self.account)
3495 gajim.ged.remove_event_handler('new-account-connected', ged.GUI1,
3496 self._nec_new_acc_connected)
3497 gajim.ged.remove_event_handler('new-account-not-connected', ged.GUI1,
3498 self._nec_new_acc_not_connected)
3499 gajim.ged.remove_event_handler('account-created', ged.GUI1,
3500 self._nec_acc_is_ok)
3501 gajim.ged.remove_event_handler('account-not-created', ged.GUI1,
3502 self._nec_acc_is_not_ok)
3503 del gajim.interface.instances['account_creation_wizard']
3505 def on_register_server_features_button_clicked(self, widget):
3506 helpers.launch_browser_mailer('url',
3507 'http://www.jabber.org/network/oldnetwork.shtml')
3509 def on_save_password_checkbutton_toggled(self, widget):
3510 self.xml.get_object('password_entry').grab_focus()
3512 def on_cancel_button_clicked(self, widget):
3513 self.window.destroy()
3515 def on_back_button_clicked(self, widget):
3516 cur_page = self.notebook.get_current_page()
3517 if cur_page in (1, 2):
3518 self.notebook.set_current_page(0)
3519 self.back_button.set_sensitive(False)
3520 elif cur_page == 3:
3521 self.xml.get_object('form_vbox').remove(self.data_form_widget)
3522 self.notebook.set_current_page(2) # show server page
3523 elif cur_page == 4:
3524 if self.account in gajim.connections:
3525 del gajim.connections[self.account]
3526 self.notebook.set_current_page(2)
3527 self.xml.get_object('form_vbox').remove(self.data_form_widget)
3528 elif cur_page == 6: # finish page
3529 self.forward_button.show()
3530 if self.modify:
3531 self.notebook.set_current_page(1) # Go to parameters page
3532 else:
3533 self.notebook.set_current_page(2) # Go to server page
3535 def on_anonymous_checkbutton1_toggled(self, widget):
3536 active = widget.get_active()
3537 self.xml.get_object('username_entry').set_sensitive(not active)
3538 self.xml.get_object('password_entry').set_sensitive(not active)
3539 self.xml.get_object('save_password_checkbutton').set_sensitive(
3540 not active)
3542 def show_finish_page(self):
3543 self.cancel_button.hide()
3544 self.back_button.hide()
3545 self.forward_button.hide()
3546 if self.modify:
3547 finish_text = '<big><b>%s</b></big>\n\n%s' % (
3548 _('Account has been added successfully'),
3549 _('You can set advanced account options by pressing the '
3550 'Advanced button, or later by choosing the Accounts menu item '
3551 'under the Edit menu from the main window.'))
3552 else:
3553 finish_text = '<big><b>%s</b></big>\n\n%s' % (
3554 _('Your new account has been created successfully'),
3555 _('You can set advanced account options by pressing the '
3556 'Advanced button, or later by choosing the Accounts menu item '
3557 'under the Edit menu from the main window.'))
3558 self.finish_label.set_markup(finish_text)
3559 self.finish_button.show()
3560 self.finish_button.set_property('has-default', True)
3561 self.advanced_button.show()
3562 self.go_online_checkbutton.show()
3563 img = self.xml.get_object('finish_image')
3564 if self.modify:
3565 img.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_DIALOG)
3566 else:
3567 path_to_file = gtkgui_helpers.get_icon_path('gajim', 48)
3568 img.set_from_file(path_to_file)
3569 self.show_vcard_checkbutton.set_active(not self.modify)
3570 self.notebook.set_current_page(6) # show finish page
3572 def on_forward_button_clicked(self, widget):
3573 cur_page = self.notebook.get_current_page()
3575 if cur_page == 0:
3576 widget = self.xml.get_object('use_existing_account_radiobutton')
3577 if widget.get_active():
3578 self.modify = True
3579 self.notebook.set_current_page(1)
3580 else:
3581 self.modify = False
3582 self.notebook.set_current_page(2)
3583 self.back_button.set_sensitive(True)
3584 return
3586 elif cur_page == 1:
3587 # We are adding an existing account
3588 anonymous = self.xml.get_object('anonymous_checkbutton1').\
3589 get_active()
3590 username = self.xml.get_object('username_entry').get_text().decode(
3591 'utf-8').strip()
3592 if not username and not anonymous:
3593 pritext = _('Invalid username')
3594 sectext = _(
3595 'You must provide a username to configure this account.')
3596 dialogs.ErrorDialog(pritext, sectext)
3597 return
3598 server = self.xml.get_object('server_comboboxentry').child.\
3599 get_text().decode('utf-8').strip()
3600 savepass = self.xml.get_object('save_password_checkbutton').\
3601 get_active()
3602 password = self.xml.get_object('password_entry').get_text().decode(
3603 'utf-8')
3605 jid = username + '@' + server
3606 # check if jid is conform to RFC and stringprep it
3607 try:
3608 jid = helpers.parse_jid(jid)
3609 except helpers.InvalidFormat, s:
3610 pritext = _('Invalid Jabber ID')
3611 dialogs.ErrorDialog(pritext, str(s))
3612 return
3614 self.account = server
3615 i = 1
3616 while self.account in gajim.connections:
3617 self.account = server + str(i)
3618 i += 1
3620 username, server = gajim.get_name_and_server_from_jid(jid)
3621 if self.xml.get_object('anonymous_checkbutton1').get_active():
3622 self.save_account('', server, False, '', anonymous=True)
3623 else:
3624 self.save_account(username, server, savepass, password)
3625 self.show_finish_page()
3626 elif cur_page == 2:
3627 # We are creating a new account
3628 server = self.xml.get_object('server_comboboxentry1').child.\
3629 get_text().decode('utf-8')
3631 if not server:
3632 dialogs.ErrorDialog(_('Invalid server'),
3633 _('Please provide a server on which you want to register.'))
3634 return
3635 self.account = server
3636 i = 1
3637 while self.account in gajim.connections:
3638 self.account = server + str(i)
3639 i += 1
3641 config = self.get_config('', server, '', '')
3642 # Get advanced options
3643 proxies_combobox = self.xml.get_object('proxies_combobox')
3644 active = proxies_combobox.get_active()
3645 proxy = proxies_combobox.get_model()[active][0].decode('utf-8')
3646 if proxy == _('None'):
3647 proxy = ''
3648 config['proxy'] = proxy
3650 config['use_custom_host'] = self.xml.get_object(
3651 'custom_host_port_checkbutton').get_active()
3652 custom_port = self.xml.get_object('custom_port_entry').get_text()
3653 try:
3654 custom_port = int(custom_port)
3655 except Exception:
3656 dialogs.ErrorDialog(_('Invalid entry'),
3657 _('Custom port must be a port number.'))
3658 return
3659 config['custom_port'] = custom_port
3660 config['custom_host'] = self.xml.get_object(
3661 'custom_host_entry').get_text().decode('utf-8')
3663 if self.xml.get_object('anonymous_checkbutton2').get_active():
3664 self.modify = True
3665 self.save_account('', server, False, '', anonymous=True)
3666 self.show_finish_page()
3667 else:
3668 self.notebook.set_current_page(5) # show creating page
3669 self.back_button.hide()
3670 self.forward_button.hide()
3671 self.update_progressbar_timeout_id = gobject.timeout_add(100,
3672 self.update_progressbar)
3673 # Get form from serveur
3674 con = connection.Connection(self.account)
3675 gajim.connections[self.account] = con
3676 con.new_account(self.account, config)
3677 elif cur_page == 3:
3678 checked = self.xml.get_object('ssl_checkbutton').get_active()
3679 if checked:
3680 hostname = gajim.connections[self.account].new_account_info[
3681 'hostname']
3682 # Check if cert is already in file
3683 certs = ''
3684 if os.path.isfile(gajim.MY_CACERTS):
3685 f = open(gajim.MY_CACERTS)
3686 certs = f.read()
3687 f.close()
3688 if self.ssl_cert in certs:
3689 dialogs.ErrorDialog(_('Certificate Already in File'),
3690 _('This certificate is already in file %s, so it\'s '
3691 'not added again.') % gajim.MY_CACERTS)
3692 else:
3693 f = open(gajim.MY_CACERTS, 'a')
3694 f.write(hostname + '\n')
3695 f.write(self.ssl_cert + '\n\n')
3696 f.close()
3697 gajim.connections[self.account].new_account_info[
3698 'ssl_fingerprint_sha1'] = self.ssl_fingerprint
3699 self.notebook.set_current_page(4) # show fom page
3700 elif cur_page == 4:
3701 if self.is_form:
3702 form = self.data_form_widget.data_form
3703 else:
3704 form = self.data_form_widget.get_infos()
3705 gajim.connections[self.account].send_new_account_infos(form,
3706 self.is_form)
3707 self.xml.get_object('form_vbox').remove(self.data_form_widget)
3708 self.xml.get_object('progressbar_label').set_markup(
3709 '<b>Account is being created</b>\n\nPlease wait...')
3710 self.notebook.set_current_page(5) # show creating page
3711 self.back_button.hide()
3712 self.forward_button.hide()
3713 self.update_progressbar_timeout_id = gobject.timeout_add(100,
3714 self.update_progressbar)
3716 def update_proxy_list(self):
3717 proxies_combobox = self.xml.get_object('proxies_combobox')
3718 model = gtk.ListStore(str)
3719 proxies_combobox.set_model(model)
3720 l = gajim.config.get_per('proxies')
3721 l.insert(0, _('None'))
3722 for i in xrange(len(l)):
3723 model.append([l[i]])
3724 proxies_combobox.set_active(0)
3726 def on_manage_proxies_button_clicked(self, widget):
3727 if 'manage_proxies' in gajim.interface.instances:
3728 gajim.interface.instances['manage_proxies'].window.present()
3729 else:
3730 gajim.interface.instances['manage_proxies'] = \
3731 ManageProxiesWindow()
3733 def on_custom_host_port_checkbutton_toggled(self, widget):
3734 self.xml.get_object('custom_host_hbox').set_sensitive(widget.\
3735 get_active())
3737 def update_progressbar(self):
3738 self.progressbar.pulse()
3739 return True # loop forever
3741 def _nec_new_acc_connected(self, obj):
3743 Connection to server succeded, present the form to the user
3745 # We receive events from all accounts from GED
3746 if obj.conn.name != self.account:
3747 return
3748 if self.update_progressbar_timeout_id is not None:
3749 gobject.source_remove(self.update_progressbar_timeout_id)
3750 self.back_button.show()
3751 self.forward_button.show()
3752 self.is_form = obj.is_form
3753 if obj.is_form:
3754 dataform = dataforms.ExtendForm(node=obj.config)
3755 self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
3756 else:
3757 self.data_form_widget = FakeDataForm(obj.config)
3758 self.data_form_widget.show_all()
3759 self.xml.get_object('form_vbox').pack_start(self.data_form_widget)
3760 self.ssl_fingerprint = obj.ssl_fingerprint
3761 self.ssl_cert = obj.ssl_cert
3762 if obj.ssl_msg:
3763 # An SSL warning occured, show it
3764 hostname = gajim.connections[self.account].new_account_info[
3765 'hostname']
3766 self.xml.get_object('ssl_label').set_markup(_(
3767 '<b>Security Warning</b>'
3768 '\n\nThe authenticity of the %(hostname)s SSL certificate could'
3769 ' be invalid.\nSSL Error: %(error)s\n'
3770 'Do you still want to connect to this server?') % {
3771 'hostname': hostname, 'error': obj.ssl_msg})
3772 if obj.errnum in (18, 27):
3773 text = _('Add this certificate to the list of trusted '
3774 'certificates.\nSHA1 fingerprint of the certificate:\n%s') \
3775 % obj.ssl_fingerprint
3776 self.xml.get_object('ssl_checkbutton').set_label(text)
3777 else:
3778 self.xml.get_object('ssl_checkbutton').set_no_show_all(True)
3779 self.xml.get_object('ssl_checkbutton').hide()
3780 self.notebook.set_current_page(3) # show SSL page
3781 else:
3782 self.notebook.set_current_page(4) # show form page
3784 def _nec_new_acc_not_connected(self, obj):
3786 Account creation failed: connection to server failed
3788 # We receive events from all accounts from GED
3789 if obj.conn.name != self.account:
3790 return
3791 if self.account not in gajim.connections:
3792 return
3793 if self.update_progressbar_timeout_id is not None:
3794 gobject.source_remove(self.update_progressbar_timeout_id)
3795 del gajim.connections[self.account]
3796 if self.account in gajim.config.get_per('accounts'):
3797 gajim.config.del_per('accounts', self.account)
3798 self.back_button.show()
3799 self.cancel_button.show()
3800 self.go_online_checkbutton.hide()
3801 self.show_vcard_checkbutton.hide()
3802 img = self.xml.get_object('finish_image')
3803 img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
3804 finish_text = '<big><b>%s</b></big>\n\n%s' % (
3805 _('An error occurred during account creation'), obj.reason)
3806 self.finish_label.set_markup(finish_text)
3807 self.notebook.set_current_page(6) # show finish page
3809 def _nec_acc_is_ok(self, obj):
3811 Account creation succeeded
3813 # We receive events from all accounts from GED
3814 if obj.conn.name != self.account:
3815 return
3816 self.create_vars(obj.account_info)
3817 self.show_finish_page()
3819 if self.update_progressbar_timeout_id is not None:
3820 gobject.source_remove(self.update_progressbar_timeout_id)
3822 def _nec_acc_is_not_ok(self, obj):
3824 Account creation failed
3826 # We receive events from all accounts from GED
3827 if obj.conn.name != self.account:
3828 return
3829 self.back_button.show()
3830 self.cancel_button.show()
3831 self.go_online_checkbutton.hide()
3832 self.show_vcard_checkbutton.hide()
3833 del gajim.connections[self.account]
3834 if self.account in gajim.config.get_per('accounts'):
3835 gajim.config.del_per('accounts', self.account)
3836 img = self.xml.get_object('finish_image')
3837 img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
3838 finish_text = '<big><b>%s</b></big>\n\n%s' % (_(
3839 'An error occurred during account creation'), obj.reason)
3840 self.finish_label.set_markup(finish_text)
3841 self.notebook.set_current_page(6) # show finish page
3843 if self.update_progressbar_timeout_id is not None:
3844 gobject.source_remove(self.update_progressbar_timeout_id)
3846 def on_advanced_button_clicked(self, widget):
3847 if 'accounts' in gajim.interface.instances:
3848 gajim.interface.instances['accounts'].window.present()
3849 else:
3850 gajim.interface.instances['accounts'] = AccountsWindow()
3851 gajim.interface.instances['accounts'].select_account(self.account)
3852 self.window.destroy()
3854 def on_finish_button_clicked(self, widget):
3855 go_online = self.xml.get_object('go_online_checkbutton').get_active()
3856 show_vcard = self.xml.get_object('show_vcard_checkbutton').get_active()
3857 self.window.destroy()
3858 if show_vcard:
3859 gajim.interface.show_vcard_when_connect.append(self.account)
3860 if go_online:
3861 gajim.interface.roster.send_status(self.account, 'online', '')
3863 def on_username_entry_key_press_event(self, widget, event):
3864 # Check for pressed @ and jump to combobox if found
3865 if event.keyval == gtk.keysyms.at:
3866 combobox = self.xml.get_object('server_comboboxentry')
3867 combobox.grab_focus()
3868 combobox.child.set_position(-1)
3869 return True
3871 def on_server_comboboxentry_key_press_event(self, widget, event, combobox):
3872 # If backspace is pressed in empty field, return to the nick entry field
3873 backspace = event.keyval == gtk.keysyms.BackSpace
3874 empty = len(combobox.get_active_text()) == 0
3875 if backspace and empty and self.modify:
3876 username_entry = self.xml.get_object('username_entry')
3877 username_entry.grab_focus()
3878 username_entry.set_position(-1)
3879 return True
3881 def get_config(self, login, server, savepass, password, anonymous=False):
3882 config = {}
3883 config['name'] = login
3884 config['hostname'] = server
3885 config['savepass'] = savepass
3886 config['password'] = password
3887 config['resource'] = 'Gajim'
3888 config['anonymous_auth'] = anonymous
3889 config['priority'] = 5
3890 config['autoconnect'] = True
3891 config['no_log_for'] = ''
3892 config['sync_with_global_status'] = True
3893 config['proxy'] = ''
3894 config['usessl'] = False
3895 config['use_custom_host'] = False
3896 config['custom_port'] = 0
3897 config['custom_host'] = ''
3898 config['keyname'] = ''
3899 config['keyid'] = ''
3900 return config
3902 def save_account(self, login, server, savepass, password, anonymous=False):
3903 if self.account in gajim.connections:
3904 dialogs.ErrorDialog(_('Account name is in use'),
3905 _('You already have an account using this name.'))
3906 return
3907 con = connection.Connection(self.account)
3908 con.password = password
3910 config = self.get_config(login, server, savepass, password, anonymous)
3912 if not self.modify:
3913 con.new_account(self.account, config)
3914 return
3915 gajim.connections[self.account] = con
3916 self.create_vars(config)
3918 def create_vars(self, config):
3919 gajim.config.add_per('accounts', self.account)
3921 if not config['savepass']:
3922 config['password'] = ''
3924 for opt in config:
3925 gajim.config.set_per('accounts', self.account, opt, config[opt])
3927 # update variables
3928 gajim.interface.instances[self.account] = {'infos': {}, 'disco': {},
3929 'gc_config': {}, 'search': {}, 'online_dialog': {}}
3930 gajim.interface.minimized_controls[self.account] = {}
3931 gajim.connections[self.account].connected = 0
3932 gajim.connections[self.account].keepalives = gajim.config.get_per(
3933 'accounts', self.account, 'keep_alive_every_foo_secs')
3934 gajim.groups[self.account] = {}
3935 gajim.contacts.add_account(self.account)
3936 gajim.gc_connected[self.account] = {}
3937 gajim.automatic_rooms[self.account] = {}
3938 gajim.newly_added[self.account] = []
3939 gajim.to_be_removed[self.account] = []
3940 gajim.nicks[self.account] = config['name']
3941 gajim.block_signed_in_notifications[self.account] = True
3942 gajim.sleeper_state[self.account] = 'off'
3943 gajim.encrypted_chats[self.account] = []
3944 gajim.last_message_time[self.account] = {}
3945 gajim.status_before_autoaway[self.account] = ''
3946 gajim.transport_avatar[self.account] = {}
3947 gajim.gajim_optional_features[self.account] = []
3948 gajim.caps_hash[self.account] = ''
3949 # refresh accounts window
3950 if 'accounts' in gajim.interface.instances:
3951 gajim.interface.instances['accounts'].init_accounts()
3952 # refresh roster
3953 if len(gajim.connections) >= 2:
3954 # Do not merge accounts if only one exists
3955 gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
3956 else:
3957 gajim.interface.roster.regroup = False
3958 gajim.interface.roster.setup_and_draw_roster()
3959 gajim.interface.roster.set_actions_menu_needs_rebuild()
3960 gajim.interface.save_config()
3962 class ManagePEPServicesWindow:
3963 def __init__(self, account):
3964 self.xml = gtkgui_helpers.get_gtk_builder('manage_pep_services_window.ui')
3965 self.window = self.xml.get_object('manage_pep_services_window')
3966 self.window.set_transient_for(gajim.interface.roster.window)
3967 self.xml.get_object('configure_button').set_sensitive(False)
3968 self.xml.get_object('delete_button').set_sensitive(False)
3969 self.xml.connect_signals(self)
3970 self.account = account
3972 self.init_services()
3973 self.xml.get_object('services_treeview').get_selection().connect(
3974 'changed', self.on_services_selection_changed)
3976 gajim.ged.register_event_handler('pep-config-received', ged.GUI1,
3977 self._nec_pep_config_received)
3978 gajim.ged.register_event_handler('agent-items-received', ged.GUI1,
3979 self._nec_agent_items_received)
3981 self.window.show_all()
3983 def on_manage_pep_services_window_destroy(self, widget):
3984 '''close window'''
3985 del gajim.interface.instances[self.account]['pep_services']
3986 gajim.ged.remove_event_handler('pep-config-received', ged.GUI1,
3987 self._nec_pep_config_received)
3988 gajim.ged.remove_event_handler('agent-items-received', ged.GUI1,
3989 self._nec_agent_items_received)
3991 def on_close_button_clicked(self, widget):
3992 self.window.destroy()
3994 def on_services_selection_changed(self, sel):
3995 self.xml.get_object('configure_button').set_sensitive(True)
3996 self.xml.get_object('delete_button').set_sensitive(True)
3998 def init_services(self):
3999 self.treeview = self.xml.get_object('services_treeview')
4000 # service, access_model, group
4001 self.treestore = gtk.ListStore(str)
4002 self.treeview.set_model(self.treestore)
4004 col = gtk.TreeViewColumn('Service')
4005 self.treeview.append_column(col)
4007 cellrenderer_text = gtk.CellRendererText()
4008 col.pack_start(cellrenderer_text)
4009 col.add_attribute(cellrenderer_text, 'text', 0)
4011 our_jid = gajim.get_jid_from_account(self.account)
4012 gajim.connections[self.account].discoverItems(our_jid)
4014 def _nec_agent_items_received(self, obj):
4015 our_jid = gajim.get_jid_from_account(self.account)
4016 for item in obj.items:
4017 if 'jid' in item and item['jid'] == our_jid and 'node' in item:
4018 self.treestore.append([item['node']])
4020 def node_removed(self, jid, node):
4021 if jid != gajim.get_jid_from_account(self.account):
4022 return
4023 model = self.treeview.get_model()
4024 iter_ = model.get_iter_root()
4025 while iter_:
4026 if model[iter_][0] == node:
4027 model.remove(iter_)
4028 break
4029 iter_ = model.iter_next(iter_)
4031 def node_not_removed(self, jid, node, msg):
4032 if jid != gajim.get_jid_from_account(self.account):
4033 return
4034 dialogs.WarningDialog(_('PEP node was not removed'),
4035 _('PEP node %(node)s was not removed: %(message)s') % {'node': node,
4036 'message': msg})
4038 def on_delete_button_clicked(self, widget):
4039 selection = self.treeview.get_selection()
4040 if not selection:
4041 return
4042 model, iter_ = selection.get_selected()
4043 node = model[iter_][0]
4044 our_jid = gajim.get_jid_from_account(self.account)
4045 gajim.connections[self.account].send_pb_delete(our_jid, node,
4046 on_ok=self.node_removed, on_fail=self.node_not_removed)
4048 def on_configure_button_clicked(self, widget):
4049 selection = self.treeview.get_selection()
4050 if not selection:
4051 return
4052 model, iter_ = selection.get_selected()
4053 node = model[iter_][0]
4054 our_jid = gajim.get_jid_from_account(self.account)
4055 gajim.connections[self.account].request_pb_configuration(our_jid, node)
4057 def _nec_pep_config_received(self, obj):
4058 def on_ok(form, node):
4059 form.type = 'submit'
4060 our_jid = gajim.get_jid_from_account(self.account)
4061 gajim.connections[self.account].send_pb_configure(our_jid, node, form)
4062 window = dialogs.DataFormWindow(obj.form, (on_ok, obj.node))
4063 title = _('Configure %s') % obj.node
4064 window.set_title(title)
4065 window.show_all()
4067 class ManageSoundsWindow:
4068 def __init__(self):
4069 self.xml = gtkgui_helpers.get_gtk_builder('manage_sounds_window.ui')
4070 self.window = self.xml.get_object('manage_sounds_window')
4072 # sounds treeview
4073 self.sound_tree = self.xml.get_object('sounds_treeview')
4075 # active, event ui name, path to sound file, event_config_name
4076 model = gtk.ListStore(bool, str, str, str)
4077 self.sound_tree.set_model(model)
4079 col = gtk.TreeViewColumn(_('Active'))
4080 self.sound_tree.append_column(col)
4081 renderer = gtk.CellRendererToggle()
4082 renderer.set_property('activatable', True)
4083 renderer.connect('toggled', self.sound_toggled_cb)
4084 col.pack_start(renderer)
4085 col.set_attributes(renderer, active = 0)
4087 col = gtk.TreeViewColumn(_('Event'))
4088 self.sound_tree.append_column(col)
4089 renderer = gtk.CellRendererText()
4090 col.pack_start(renderer)
4091 col.set_attributes(renderer, text = 1)
4093 self.fill_sound_treeview()
4095 self.xml.connect_signals(self)
4097 self.sound_tree.get_model().connect('row-changed',
4098 self.on_sounds_treemodel_row_changed)
4100 self.window.show_all()
4102 def on_sounds_treemodel_row_changed(self, model, path, iter_):
4103 sound_event = model[iter_][3].decode('utf-8')
4104 gajim.config.set_per('soundevents', sound_event, 'enabled',
4105 bool(model[path][0]))
4106 gajim.config.set_per('soundevents', sound_event, 'path',
4107 model[iter_][2].decode('utf-8'))
4108 gajim.interface.save_config()
4110 def sound_toggled_cb(self, cell, path):
4111 model = self.sound_tree.get_model()
4112 model[path][0] = not model[path][0]
4114 def fill_sound_treeview(self):
4115 model = self.sound_tree.get_model()
4116 model.clear()
4117 model.set_sort_column_id(1, gtk.SORT_ASCENDING)
4119 # NOTE: sounds_ui_names MUST have all items of
4120 # sounds = gajim.config.get_per('soundevents') as keys
4121 sounds_dict = {
4122 'first_message_received': _('First Message Received'),
4123 'next_message_received_focused': _('Next Message Received Focused'),
4124 'next_message_received_unfocused':
4125 _('Next Message Received Unfocused'),
4126 'contact_connected': _('Contact Connected'),
4127 'contact_disconnected': _('Contact Disconnected'),
4128 'message_sent': _('Message Sent'),
4129 'muc_message_highlight': _('Group Chat Message Highlight'),
4130 'muc_message_received': _('Group Chat Message Received'),
4131 'gmail_received': _('GMail Email Received')
4134 for sound_event_config_name, sound_ui_name in sounds_dict.items():
4135 enabled = gajim.config.get_per('soundevents',
4136 sound_event_config_name, 'enabled')
4137 path = gajim.config.get_per('soundevents',
4138 sound_event_config_name, 'path')
4139 model.append((enabled, sound_ui_name, path, sound_event_config_name))
4141 def on_treeview_sounds_cursor_changed(self, widget, data = None):
4142 (model, iter_) = self.sound_tree.get_selection().get_selected()
4143 sounds_entry = self.xml.get_object('sounds_entry')
4144 if not iter_:
4145 sounds_entry.set_text('')
4146 return
4147 path_to_snd_file = model[iter_][2]
4148 sounds_entry.set_text(path_to_snd_file)
4150 def on_browse_for_sounds_button_clicked(self, widget, data = None):
4151 (model, iter_) = self.sound_tree.get_selection().get_selected()
4152 if not iter_:
4153 return
4154 def on_ok(widget, path_to_snd_file):
4155 self.dialog.destroy()
4156 model, iter_ = self.sound_tree.get_selection().get_selected()
4157 if not path_to_snd_file:
4158 model[iter_][2] = ''
4159 self.xml.get_object('sounds_entry').set_text('')
4160 model[iter_][0] = False
4161 return
4162 directory = os.path.dirname(path_to_snd_file)
4163 gajim.config.set('last_sounds_dir', directory)
4164 path_to_snd_file = helpers.strip_soundfile_path(path_to_snd_file)
4165 self.xml.get_object('sounds_entry').set_text(path_to_snd_file)
4167 model[iter_][2] = path_to_snd_file # set new path to sounds_model
4168 model[iter_][0] = True # set the sound to enabled
4170 def on_cancel(widget):
4171 self.dialog.destroy()
4173 path_to_snd_file = model[iter_][2].decode('utf-8')
4174 self.dialog = dialogs.SoundChooserDialog(path_to_snd_file, on_ok,
4175 on_cancel)
4177 def on_sounds_entry_changed(self, widget):
4178 path_to_snd_file = widget.get_text()
4179 model, iter_ = self.sound_tree.get_selection().get_selected()
4180 model[iter_][2] = path_to_snd_file # set new path to sounds_model
4182 def on_play_button_clicked(self, widget):
4183 model, iter_ = self.sound_tree.get_selection().get_selected()
4184 if not iter_:
4185 return
4186 snd_event_config_name = model[iter_][3]
4187 helpers.play_sound(snd_event_config_name)
4189 def on_close_button_clicked(self, widget):
4190 self.window.hide()
4192 def on_manage_sounds_window_delete_event(self, widget, event):
4193 self.window.hide()
4194 return True # do NOT destroy the window