Added AgiliaLinux to known distributions. Fix #6033
[gajim.git] / src / dialogs.py
blobbb68055b71f830e3eab4726e649f9ce3cf69adc4
1 # -*- coding: utf-8 -*-
2 ## src/dialogs.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 Mauer <hawke AT hawkesnest.net>
7 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
8 ## Travis Shirk <travis AT pobox.com>
9 ## Copyright (C) 2005-2008 Nikos Kouremenos <kourem AT gmail.com>
10 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
11 ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
12 ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
13 ## Julien Pivotto <roidelapluie AT gmail.com>
14 ## Stephan Erb <steve-e AT h3c.de>
15 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
17 ## This file is part of Gajim.
19 ## Gajim is free software; you can redistribute it and/or modify
20 ## it under the terms of the GNU General Public License as published
21 ## by the Free Software Foundation; version 3 only.
23 ## Gajim is distributed in the hope that it will be useful,
24 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
25 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 ## GNU General Public License for more details.
28 ## You should have received a copy of the GNU General Public License
29 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
32 import gtk
33 import gobject
34 import os
36 import gtkgui_helpers
37 import vcard
38 import conversation_textview
39 import message_control
40 import dataforms_widget
41 import disco
43 from random import randrange
44 from common import pep
45 from common import ged
47 try:
48 import gtkspell
49 HAS_GTK_SPELL = True
50 except ImportError:
51 HAS_GTK_SPELL = False
53 # those imports are not used in this file, but in files that 'import dialogs'
54 # so they can do dialog.GajimThemesWindow() for example
55 from filetransfers_window import FileTransfersWindow
56 from gajim_themes_window import GajimThemesWindow
57 from advanced_configuration_window import AdvancedConfigurationWindow
59 from common import gajim
60 from common import helpers
61 from common import dataforms
62 from common.exceptions import GajimGeneralException
64 class EditGroupsDialog:
65 """
66 Class for the edit group dialog window
67 """
69 def __init__(self, list_):
70 """
71 list_ is a list of (contact, account) tuples
72 """
73 self.xml = gtkgui_helpers.get_gtk_builder('edit_groups_dialog.ui')
74 self.dialog = self.xml.get_object('edit_groups_dialog')
75 self.dialog.set_transient_for(gajim.interface.roster.window)
76 self.list_ = list_
77 self.changes_made = False
78 self.treeview = self.xml.get_object('groups_treeview')
79 if len(list_) == 1:
80 contact = list_[0][0]
81 self.xml.get_object('nickname_label').set_markup(
82 _('Contact name: <i>%s</i>') % contact.get_shown_name())
83 self.xml.get_object('jid_label').set_markup(
84 _('Jabber ID: <i>%s</i>') % contact.jid)
85 else:
86 self.xml.get_object('nickname_label').set_no_show_all(True)
87 self.xml.get_object('nickname_label').hide()
88 self.xml.get_object('jid_label').set_no_show_all(True)
89 self.xml.get_object('jid_label').hide()
91 self.xml.connect_signals(self)
92 self.init_list()
94 self.dialog.show_all()
95 if self.changes_made:
96 for (contact, account) in self.list_:
97 gajim.connections[account].update_contact(contact.jid, contact.name,
98 contact.groups)
100 def on_edit_groups_dialog_response(self, widget, response_id):
101 if response_id == gtk.RESPONSE_CLOSE:
102 self.dialog.destroy()
104 def remove_group(self, group):
106 Remove group group from all contacts and all their brothers
108 for (contact, account) in self.list_:
109 gajim.interface.roster.remove_contact_from_groups(contact.jid, account, [group])
111 # FIXME: Ugly workaround.
112 gajim.interface.roster.draw_group(_('General'), account)
114 def add_group(self, group):
116 Add group group to all contacts and all their brothers
118 for (contact, account) in self.list_:
119 gajim.interface.roster.add_contact_to_groups(contact.jid, account, [group])
121 # FIXME: Ugly workaround. Maybe we haven't been in any group (defaults to General)
122 gajim.interface.roster.draw_group(_('General'), account)
124 def on_add_button_clicked(self, widget):
125 group = self.xml.get_object('group_entry').get_text().decode('utf-8')
126 if not group:
127 return
128 # Do not allow special groups
129 if group in helpers.special_groups:
130 return
131 # check if it already exists
132 model = self.treeview.get_model()
133 iter_ = model.get_iter_root()
134 while iter_:
135 if model.get_value(iter_, 0).decode('utf-8') == group:
136 return
137 iter_ = model.iter_next(iter_)
138 self.changes_made = True
139 model.append((group, True, False))
140 self.add_group(group)
141 self.init_list() # Re-draw list to sort new item
143 def group_toggled_cb(self, cell, path):
144 self.changes_made = True
145 model = self.treeview.get_model()
146 if model[path][2]:
147 model[path][2] = False
148 model[path][1] = True
149 else:
150 model[path][1] = not model[path][1]
151 group = model[path][0].decode('utf-8')
152 if model[path][1]:
153 self.add_group(group)
154 else:
155 self.remove_group(group)
157 def init_list(self):
158 store = gtk.ListStore(str, bool, bool)
159 self.treeview.set_model(store)
160 for column in self.treeview.get_columns():
161 # Clear treeview when re-drawing
162 self.treeview.remove_column(column)
163 accounts = []
164 # Store groups in a list so we can sort them and the number of contacts in
165 # it
166 groups = {}
167 for (contact, account) in self.list_:
168 if account not in accounts:
169 accounts.append(account)
170 for g in gajim.groups[account].keys():
171 if g in groups:
172 continue
173 groups[g] = 0
174 c_groups = contact.groups
175 for g in c_groups:
176 groups[g] += 1
177 group_list = []
178 # Remove special groups if they are empty
179 for group in groups:
180 if group not in helpers.special_groups or groups[group] > 0:
181 group_list.append(group)
182 group_list.sort()
183 for group in group_list:
184 iter_ = store.append()
185 store.set(iter_, 0, group) # Group name
186 if groups[group] == 0:
187 store.set(iter_, 1, False)
188 else:
189 store.set(iter_, 1, True)
190 if groups[group] == len(self.list_):
191 # all contacts are in this group
192 store.set(iter_, 2, False)
193 else:
194 store.set(iter_, 2, True)
195 column = gtk.TreeViewColumn(_('Group'))
196 column.set_expand(True)
197 self.treeview.append_column(column)
198 renderer = gtk.CellRendererText()
199 column.pack_start(renderer)
200 column.set_attributes(renderer, text=0)
202 column = gtk.TreeViewColumn(_('In the group'))
203 column.set_expand(False)
204 self.treeview.append_column(column)
205 renderer = gtk.CellRendererToggle()
206 column.pack_start(renderer)
207 renderer.set_property('activatable', True)
208 renderer.connect('toggled', self.group_toggled_cb)
209 column.set_attributes(renderer, active=1, inconsistent=2)
211 class PassphraseDialog:
213 Class for Passphrase dialog
215 def __init__(self, titletext, labeltext, checkbuttontext=None,
216 ok_handler=None, cancel_handler=None):
217 self.xml = gtkgui_helpers.get_gtk_builder('passphrase_dialog.ui')
218 self.window = self.xml.get_object('passphrase_dialog')
219 self.passphrase_entry = self.xml.get_object('passphrase_entry')
220 self.passphrase = -1
221 self.window.set_title(titletext)
222 self.xml.get_object('message_label').set_text(labeltext)
224 self.ok = False
226 self.cancel_handler = cancel_handler
227 self.ok_handler = ok_handler
228 okbutton = self.xml.get_object('ok_button')
229 okbutton.connect('clicked', self.on_okbutton_clicked)
230 cancelbutton = self.xml.get_object('cancel_button')
231 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
233 self.xml.connect_signals(self)
234 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
235 self.window.show_all()
237 self.check = bool(checkbuttontext)
238 checkbutton = self.xml.get_object('save_passphrase_checkbutton')
239 if self.check:
240 checkbutton.set_label(checkbuttontext)
241 else:
242 checkbutton.hide()
244 def on_okbutton_clicked(self, widget):
245 if not self.ok_handler:
246 return
248 passph = self.passphrase_entry.get_text().decode('utf-8')
250 if self.check:
251 checked = self.xml.get_object('save_passphrase_checkbutton').\
252 get_active()
253 else:
254 checked = False
256 self.ok = True
258 self.window.destroy()
260 if isinstance(self.ok_handler, tuple):
261 self.ok_handler[0](passph, checked, *self.ok_handler[1:])
262 else:
263 self.ok_handler(passph, checked)
265 def on_cancelbutton_clicked(self, widget):
266 self.window.destroy()
268 def on_passphrase_dialog_destroy(self, widget):
269 if self.cancel_handler and not self.ok:
270 self.cancel_handler()
272 class ChooseGPGKeyDialog:
274 Class for GPG key dialog
277 def __init__(self, title_text, prompt_text, secret_keys, on_response,
278 selected=None):
279 '''secret_keys : {keyID: userName, ...}'''
280 self.on_response = on_response
281 xml = gtkgui_helpers.get_gtk_builder('choose_gpg_key_dialog.ui')
282 self.window = xml.get_object('choose_gpg_key_dialog')
283 self.window.set_title(title_text)
284 self.keys_treeview = xml.get_object('keys_treeview')
285 prompt_label = xml.get_object('prompt_label')
286 prompt_label.set_text(prompt_text)
287 model = gtk.ListStore(str, str)
288 model.set_sort_func(1, self.sort_keys)
289 model.set_sort_column_id(1, gtk.SORT_ASCENDING)
290 self.keys_treeview.set_model(model)
291 #columns
292 renderer = gtk.CellRendererText()
293 col = self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
294 renderer, text=0)
295 col.set_sort_column_id(0)
296 renderer = gtk.CellRendererText()
297 col = self.keys_treeview.insert_column_with_attributes(-1,
298 _('Contact name'), renderer, text=1)
299 col.set_sort_column_id(1)
300 self.keys_treeview.set_search_column(1)
301 self.fill_tree(secret_keys, selected)
302 self.window.connect('response', self.on_dialog_response)
303 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
304 self.window.show_all()
306 def sort_keys(self, model, iter1, iter2):
307 value1 = model[iter1][1]
308 value2 = model[iter2][1]
309 if value1 == _('None'):
310 return -1
311 elif value2 == _('None'):
312 return 1
313 elif value1 < value2:
314 return -1
315 return 1
317 def on_dialog_response(self, dialog, response):
318 selection = self.keys_treeview.get_selection()
319 (model, iter_) = selection.get_selected()
320 if iter_ and response == gtk.RESPONSE_OK:
321 keyID = [ model[iter_][0].decode('utf-8'),
322 model[iter_][1].decode('utf-8') ]
323 else:
324 keyID = None
325 self.on_response(keyID)
326 self.window.destroy()
328 def fill_tree(self, list_, selected):
329 model = self.keys_treeview.get_model()
330 for keyID in list_.keys():
331 iter_ = model.append((keyID, list_[keyID]))
332 if keyID == selected:
333 path = model.get_path(iter_)
334 self.keys_treeview.set_cursor(path)
337 class ChangeActivityDialog:
338 PAGELIST = ['doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
339 'having_appointment', 'inactive', 'relaxing', 'talking', 'traveling',
340 'working']
342 def __init__(self, on_response, activity=None, subactivity=None, text=''):
343 self.on_response = on_response
344 self.activity = activity
345 self.subactivity = subactivity
346 self.text = text
347 self.xml = gtkgui_helpers.get_gtk_builder('change_activity_dialog.ui')
348 self.window = self.xml.get_object('change_activity_dialog')
349 self.window.set_transient_for(gajim.interface.roster.window)
351 self.checkbutton = self.xml.get_object('enable_checkbutton')
352 self.notebook = self.xml.get_object('notebook')
353 self.entry = self.xml.get_object('description_entry')
355 rbtns = {}
356 group = None
358 for category in pep.ACTIVITIES:
359 item = self.xml.get_object(category + '_image')
360 item.set_from_pixbuf(
361 gtkgui_helpers.load_activity_icon(category).get_pixbuf())
362 item.set_tooltip_text(pep.ACTIVITIES[category]['category'])
364 vbox = self.xml.get_object(category + '_vbox')
365 vbox.set_border_width(5)
367 # Other
368 act = category + '_other'
370 if group:
371 rbtns[act] = gtk.RadioButton(group)
372 else:
373 rbtns[act] = group = gtk.RadioButton()
375 hbox = gtk.HBox(False, 5)
376 hbox.pack_start(gtkgui_helpers.load_activity_icon(category), False,
377 False, 0)
378 lbl = gtk.Label('<b>' + pep.ACTIVITIES[category]['category'] + '</b>')
379 lbl.set_use_markup(True)
380 hbox.pack_start(lbl, False, False, 0)
381 rbtns[act].add(hbox)
382 rbtns[act].connect('toggled', self.on_rbtn_toggled,
383 [category, 'other'])
384 vbox.pack_start(rbtns[act], False, False, 0)
386 activities = []
387 for activity in pep.ACTIVITIES[category]:
388 activities.append(activity)
389 activities.sort()
391 for activity in activities:
392 if activity == 'category':
393 continue
395 act = category + '_' + activity
397 if group:
398 rbtns[act] = gtk.RadioButton(group)
399 else:
400 rbtns[act] = group = gtk.RadioButton()
402 hbox = gtk.HBox(False, 5)
403 hbox.pack_start(gtkgui_helpers.load_activity_icon(category,
404 activity), False, False, 0)
405 hbox.pack_start(gtk.Label(pep.ACTIVITIES[category][activity]),
406 False, False, 0)
407 rbtns[act].connect('toggled', self.on_rbtn_toggled,
408 [category, activity])
409 rbtns[act].add(hbox)
410 vbox.pack_start(rbtns[act], False, False, 0)
413 self.default_radio = rbtns['doing_chores_other']
415 if self.activity in pep.ACTIVITIES:
416 if not self.subactivity in pep.ACTIVITIES[self.activity]:
417 self.subactivity = 'other'
419 rbtns[self.activity + '_' + self.subactivity].set_active(True)
421 self.checkbutton.set_active(True)
422 self.notebook.set_sensitive(True)
423 self.entry.set_sensitive(True)
425 self.notebook.set_current_page(
426 self.PAGELIST.index(self.activity))
428 self.entry.set_text(text)
430 else:
431 self.checkbutton.set_active(False)
433 self.xml.connect_signals(self)
434 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
435 self.window.show_all()
437 def on_enable_checkbutton_toggled(self, widget):
438 self.notebook.set_sensitive(widget.get_active())
439 self.entry.set_sensitive(widget.get_active())
440 if not self.activity:
441 self.default_radio.set_active(True)
443 def on_rbtn_toggled(self, widget, data):
444 if widget.get_active():
445 self.activity = data[0]
446 self.subactivity = data[1]
448 def on_ok_button_clicked(self, widget):
450 Return activity and messsage (None if no activity selected)
452 if self.checkbutton.get_active():
453 self.on_response(self.activity, self.subactivity,
454 self.entry.get_text().decode('utf-8'))
455 else:
456 self.on_response(None, None, '')
457 self.window.destroy()
459 def on_cancel_button_clicked(self, widget):
460 self.window.destroy()
462 class ChangeMoodDialog:
463 COLS = 11
465 def __init__(self, on_response, mood=None, text=''):
466 self.on_response = on_response
467 self.mood = mood
468 self.text = text
469 self.xml = gtkgui_helpers.get_gtk_builder('change_mood_dialog.ui')
471 self.window = self.xml.get_object('change_mood_dialog')
472 self.window.set_transient_for(gajim.interface.roster.window)
473 self.window.set_title(_('Set Mood'))
475 table = self.xml.get_object('mood_icons_table')
476 self.label = self.xml.get_object('mood_label')
477 self.entry = self.xml.get_object('description_entry')
479 no_mood_button = self.xml.get_object('no_mood_button')
480 no_mood_button.set_mode(False)
481 no_mood_button.connect('clicked',
482 self.on_mood_button_clicked, None)
484 x = 1
485 y = 0
486 self.mood_buttons = {}
488 # Order them first
489 self.MOODS = []
490 for mood in pep.MOODS:
491 self.MOODS.append(mood)
492 self.MOODS.sort()
494 for mood in self.MOODS:
495 self.mood_buttons[mood] = gtk.RadioButton(no_mood_button)
496 self.mood_buttons[mood].set_mode(False)
497 self.mood_buttons[mood].add(gtkgui_helpers.load_mood_icon(mood))
498 self.mood_buttons[mood].set_relief(gtk.RELIEF_NONE)
499 self.mood_buttons[mood].set_tooltip_text(pep.MOODS[mood])
500 self.mood_buttons[mood].connect('clicked',
501 self.on_mood_button_clicked, mood)
502 table.attach(self.mood_buttons[mood], x, x + 1, y, y + 1)
504 # Calculate the next position
505 x += 1
506 if x >= self.COLS:
507 x = 0
508 y += 1
510 if self.mood in pep.MOODS:
511 self.mood_buttons[self.mood].set_active(True)
512 self.label.set_text(pep.MOODS[self.mood])
513 self.entry.set_sensitive(True)
514 if self.text:
515 self.entry.set_text(self.text)
516 else:
517 self.label.set_text(_('None'))
518 self.entry.set_text('')
519 self.entry.set_sensitive(False)
521 self.xml.connect_signals(self)
522 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
523 self.window.show_all()
525 def on_mood_button_clicked(self, widget, data):
526 if data:
527 self.label.set_text(pep.MOODS[data])
528 self.entry.set_sensitive(True)
529 else:
530 self.label.set_text(_('None'))
531 self.entry.set_text('')
532 self.entry.set_sensitive(False)
533 self.mood = data
535 def on_ok_button_clicked(self, widget):
536 '''Return mood and messsage (None if no mood selected)'''
537 message = self.entry.get_text().decode('utf-8')
538 self.on_response(self.mood, message)
539 self.window.destroy()
541 def on_cancel_button_clicked(self, widget):
542 self.window.destroy()
544 class TimeoutDialog:
546 Class designed to be derivated to create timeout'd dialogs (dialogs that
547 closes automatically after a timeout)
549 def __init__(self, timeout, on_timeout):
550 self.countdown_left = timeout
551 self.countdown_enabled = True
552 self.title_text = ''
553 self.on_timeout = on_timeout
555 def run_timeout(self):
556 if self.countdown_left > 0:
557 self.countdown()
558 gobject.timeout_add_seconds(1, self.countdown)
560 def on_timeout():
562 To be implemented in derivated classes
564 pass
566 def countdown(self):
567 if self.countdown_enabled:
568 if self.countdown_left <= 0:
569 self.on_timeout()
570 return False
571 self.dialog.set_title('%s [%s]' % (self.title_text,
572 str(self.countdown_left)))
573 self.countdown_left -= 1
574 return True
575 else:
576 self.dialog.set_title(self.title_text)
577 return False
579 class ChangeStatusMessageDialog(TimeoutDialog):
580 def __init__(self, on_response, show=None, show_pep=True):
581 countdown_time = gajim.config.get('change_status_window_timeout')
582 TimeoutDialog.__init__(self, countdown_time, self.on_timeout)
583 self.show = show
584 self.pep_dict = {}
585 self.show_pep = show_pep
586 self.on_response = on_response
587 self.xml = gtkgui_helpers.get_gtk_builder('change_status_message_dialog.ui')
588 self.dialog = self.xml.get_object('change_status_message_dialog')
589 self.dialog.set_transient_for(gajim.interface.roster.window)
590 msg = None
591 if show:
592 uf_show = helpers.get_uf_show(show)
593 self.title_text = _('%s Status Message') % uf_show
594 msg = gajim.config.get_per('statusmsg', '_last_' + self.show,
595 'message')
596 self.pep_dict['activity'] = gajim.config.get_per('statusmsg',
597 '_last_' + self.show, 'activity')
598 self.pep_dict['subactivity'] = gajim.config.get_per('statusmsg',
599 '_last_' + self.show, 'subactivity')
600 self.pep_dict['activity_text'] = gajim.config.get_per('statusmsg',
601 '_last_' + self.show, 'activity_text')
602 self.pep_dict['mood'] = gajim.config.get_per('statusmsg',
603 '_last_' + self.show, 'mood')
604 self.pep_dict['mood_text'] = gajim.config.get_per('statusmsg',
605 '_last_' + self.show, 'mood_text')
606 else:
607 self.title_text = _('Status Message')
608 self.dialog.set_title(self.title_text)
610 message_textview = self.xml.get_object('message_textview')
611 self.message_buffer = message_textview.get_buffer()
612 self.message_buffer.connect('changed', self.on_message_buffer_changed)
613 if not msg:
614 msg = ''
615 msg = helpers.from_one_line(msg)
616 self.message_buffer.set_text(msg)
618 # have an empty string selectable, so user can clear msg
619 self.preset_messages_dict = {'': ['', '', '', '', '', '']}
620 for msg_name in gajim.config.get_per('statusmsg'):
621 if msg_name.startswith('_last_'):
622 continue
623 opts = []
624 for opt in ['message', 'activity', 'subactivity', 'activity_text',
625 'mood', 'mood_text']:
626 opts.append(gajim.config.get_per('statusmsg', msg_name, opt))
627 opts[0] = helpers.from_one_line(opts[0])
628 self.preset_messages_dict[msg_name] = opts
629 sorted_keys_list = helpers.get_sorted_keys(self.preset_messages_dict)
631 self.message_liststore = gtk.ListStore(str) # msg_name
632 self.message_combobox = self.xml.get_object('message_combobox')
633 self.message_combobox.set_model(self.message_liststore)
634 cellrenderertext = gtk.CellRendererText()
635 self.message_combobox.pack_start(cellrenderertext, True)
636 self.message_combobox.add_attribute(cellrenderertext, 'text', 0)
637 for msg_name in sorted_keys_list:
638 self.message_liststore.append((msg_name,))
640 if show_pep:
641 self.draw_activity()
642 self.draw_mood()
643 else:
644 # remove acvtivity / mood lines
645 self.xml.get_object('activity_label').set_no_show_all(True)
646 self.xml.get_object('activity_button').set_no_show_all(True)
647 self.xml.get_object('mood_label').set_no_show_all(True)
648 self.xml.get_object('mood_button').set_no_show_all(True)
649 self.xml.get_object('activity_label').hide()
650 self.xml.get_object('activity_button').hide()
651 self.xml.get_object('mood_label').hide()
652 self.xml.get_object('mood_button').hide()
654 self.xml.connect_signals(self)
655 self.run_timeout()
656 self.dialog.connect('response', self.on_dialog_response)
657 self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
658 self.dialog.show_all()
660 def draw_activity(self):
662 Set activity button
664 img = self.xml.get_object('activity_image')
665 label = self.xml.get_object('activity_button_label')
666 if 'activity' in self.pep_dict and self.pep_dict['activity'] in \
667 pep.ACTIVITIES:
668 if 'subactivity' in self.pep_dict and self.pep_dict['subactivity'] \
669 in pep.ACTIVITIES[self.pep_dict['activity']]:
670 img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
671 self.pep_dict['activity'], self.pep_dict['subactivity']).\
672 get_pixbuf())
673 else:
674 img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
675 self.pep_dict['activity']).get_pixbuf())
676 if self.pep_dict['activity_text']:
677 label.set_text(self.pep_dict['activity_text'])
678 else:
679 label.set_text('')
680 else:
681 img.set_from_pixbuf(None)
682 label.set_text('')
684 def draw_mood(self):
686 Set mood button
688 img = self.xml.get_object('mood_image')
689 label = self.xml.get_object('mood_button_label')
690 if 'mood' in self.pep_dict and self.pep_dict['mood'] in pep.MOODS:
691 img.set_from_pixbuf(gtkgui_helpers.load_mood_icon(
692 self.pep_dict['mood']).get_pixbuf())
693 if self.pep_dict['mood_text']:
694 label.set_text(self.pep_dict['mood_text'])
695 else:
696 label.set_text('')
697 else:
698 img.set_from_pixbuf(None)
699 label.set_text('')
701 def on_timeout(self):
702 # Prevent GUI freeze when the combobox menu is opened on close
703 self.message_combobox.popdown()
704 self.dialog.response(gtk.RESPONSE_OK)
706 def on_dialog_response(self, dialog, response):
707 if response == gtk.RESPONSE_OK:
708 beg, end = self.message_buffer.get_bounds()
709 message = self.message_buffer.get_text(beg, end).decode('utf-8')\
710 .strip()
711 message = helpers.remove_invalid_xml_chars(message)
712 msg = helpers.to_one_line(message)
713 if self.show:
714 gajim.config.set_per('statusmsg', '_last_' + self.show,
715 'message', msg)
716 if self.show_pep:
717 gajim.config.set_per('statusmsg', '_last_' + self.show,
718 'activity', self.pep_dict['activity'])
719 gajim.config.set_per('statusmsg', '_last_' + self.show,
720 'subactivity', self.pep_dict['subactivity'])
721 gajim.config.set_per('statusmsg', '_last_' + self.show,
722 'activity_text', self.pep_dict['activity_text'])
723 gajim.config.set_per('statusmsg', '_last_' + self.show,
724 'mood', self.pep_dict['mood'])
725 gajim.config.set_per('statusmsg', '_last_' + self.show,
726 'mood_text', self.pep_dict['mood_text'])
727 else:
728 message = None # user pressed Cancel button or X wm button
729 self.dialog.destroy()
730 self.on_response(message, self.pep_dict)
732 def on_message_combobox_changed(self, widget):
733 self.countdown_enabled = False
734 model = widget.get_model()
735 active = widget.get_active()
736 if active < 0:
737 return None
738 name = model[active][0].decode('utf-8')
739 self.message_buffer.set_text(self.preset_messages_dict[name][0])
740 self.pep_dict['activity'] = self.preset_messages_dict[name][1]
741 self.pep_dict['subactivity'] = self.preset_messages_dict[name][2]
742 self.pep_dict['activity_text'] = self.preset_messages_dict[name][3]
743 self.pep_dict['mood'] = self.preset_messages_dict[name][4]
744 self.pep_dict['mood_text'] = self.preset_messages_dict[name][5]
745 self.draw_activity()
746 self.draw_mood()
748 def on_change_status_message_dialog_key_press_event(self, widget, event):
749 self.countdown_enabled = False
750 if event.keyval == gtk.keysyms.Return or \
751 event.keyval == gtk.keysyms.KP_Enter: # catch CTRL+ENTER
752 if (event.state & gtk.gdk.CONTROL_MASK):
753 self.dialog.response(gtk.RESPONSE_OK)
754 # Stop the event
755 return True
757 def on_message_buffer_changed(self, widget):
758 self.countdown_enabled = False
759 self.toggle_sensitiviy_of_save_as_preset()
761 def toggle_sensitiviy_of_save_as_preset(self):
762 btn = self.xml.get_object('save_as_preset_button')
763 if self.message_buffer.get_char_count() == 0:
764 btn.set_sensitive(False)
765 else:
766 btn.set_sensitive(True)
768 def on_save_as_preset_button_clicked(self, widget):
769 self.countdown_enabled = False
770 start_iter, finish_iter = self.message_buffer.get_bounds()
771 status_message_to_save_as_preset = self.message_buffer.get_text(
772 start_iter, finish_iter)
773 def on_ok(msg_name):
774 msg_text = status_message_to_save_as_preset.decode('utf-8')
775 msg_text_1l = helpers.to_one_line(msg_text)
776 if not msg_name: # msg_name was ''
777 msg_name = msg_text_1l.decode('utf-8')
779 def on_ok2():
780 self.preset_messages_dict[msg_name] = [
781 msg_text, self.pep_dict.get('activity'),
782 self.pep_dict.get('subactivity'),
783 self.pep_dict.get('activity_text'),
784 self.pep_dict.get('mood'), self.pep_dict.get('mood_text')]
785 gajim.config.set_per('statusmsg', msg_name, 'message',
786 msg_text_1l)
787 gajim.config.set_per('statusmsg', msg_name, 'activity',
788 self.pep_dict.get('activity'))
789 gajim.config.set_per('statusmsg', msg_name, 'subactivity',
790 self.pep_dict.get('subactivity'))
791 gajim.config.set_per('statusmsg', msg_name, 'activity_text',
792 self.pep_dict.get('activity_text'))
793 gajim.config.set_per('statusmsg', msg_name, 'mood',
794 self.pep_dict.get('mood'))
795 gajim.config.set_per('statusmsg', msg_name, 'mood_text',
796 self.pep_dict.get('mood_text'))
797 if msg_name in self.preset_messages_dict:
798 ConfirmationDialog(_('Overwrite Status Message?'),
799 _('This name is already used. Do you want to overwrite this '
800 'status message?'), on_response_ok=on_ok2)
801 return
802 gajim.config.add_per('statusmsg', msg_name)
803 on_ok2()
804 iter_ = self.message_liststore.append((msg_name,))
805 # select in combobox the one we just saved
806 self.message_combobox.set_active_iter(iter_)
807 InputDialog(_('Save as Preset Status Message'),
808 _('Please type a name for this status message'), is_modal=False,
809 ok_handler=on_ok)
811 def on_activity_button_clicked(self, widget):
812 self.countdown_enabled = False
813 def on_response(activity, subactivity, text):
814 self.pep_dict['activity'] = activity or ''
815 self.pep_dict['subactivity'] = subactivity or ''
816 self.pep_dict['activity_text'] = text
817 self.draw_activity()
818 ChangeActivityDialog(on_response, self.pep_dict['activity'],
819 self.pep_dict['subactivity'], self.pep_dict['activity_text'])
821 def on_mood_button_clicked(self, widget):
822 self.countdown_enabled = False
823 def on_response(mood, text):
824 self.pep_dict['mood'] = mood or ''
825 self.pep_dict['mood_text'] = text
826 self.draw_mood()
827 ChangeMoodDialog(on_response, self.pep_dict['mood'],
828 self.pep_dict['mood_text'])
830 class AddNewContactWindow:
832 Class for AddNewContactWindow
835 uid_labels = {'jabber': _('Jabber ID:'),
836 'aim': _('AIM Address:'),
837 'gadu-gadu': _('GG Number:'),
838 'icq': _('ICQ Number:'),
839 'msn': _('MSN Address:'),
840 'yahoo': _('Yahoo! Address:')}
842 def __init__(self, account=None, jid=None, user_nick=None, group=None):
843 self.account = account
844 if account is None:
845 # fill accounts with active accounts
846 accounts = []
847 for account in gajim.connections.keys():
848 if gajim.connections[account].connected > 1:
849 accounts.append(account)
850 if not accounts:
851 return
852 if len(accounts) == 1:
853 self.account = account
854 else:
855 accounts = [self.account]
856 if self.account:
857 location = gajim.interface.instances[self.account]
858 else:
859 location = gajim.interface.instances
860 if 'add_contact' in location:
861 location['add_contact'].window.present()
862 # An instance is already opened
863 return
864 location['add_contact'] = self
865 self.xml = gtkgui_helpers.get_gtk_builder('add_new_contact_window.ui')
866 self.xml.connect_signals(self)
867 self.window = self.xml.get_object('add_new_contact_window')
868 for w in ('account_combobox', 'account_hbox', 'account_label',
869 'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
870 'protocol_hbox', 'nickname_entry', 'message_scrolledwindow',
871 'save_message_checkbutton', 'register_hbox', 'subscription_table',
872 'add_button', 'message_textview', 'connected_label',
873 'group_comboboxentry', 'auto_authorize_checkbutton'):
874 self.__dict__[w] = self.xml.get_object(w)
875 if account and len(gajim.connections) >= 2:
876 prompt_text = \
877 _('Please fill in the data of the contact you want to add in account %s') % account
878 else:
879 prompt_text = \
880 _('Please fill in the data of the contact you want to add')
881 self.xml.get_object('prompt_label').set_text(prompt_text)
882 self.agents = {'jabber': []}
883 # types to which we are not subscribed but account has an agent for it
884 self.available_types = []
885 for acct in accounts:
886 for j in gajim.contacts.get_jid_list(acct):
887 if gajim.jid_is_transport(j):
888 type_ = gajim.get_transport_name_from_jid(j, False)
889 if not type_:
890 continue
891 if type_ in self.agents:
892 self.agents[type_].append(j)
893 else:
894 self.agents[type_] = [j]
895 # Now add the one to which we can register
896 for acct in accounts:
897 for type_ in gajim.connections[acct].available_transports:
898 if type_ in self.agents:
899 continue
900 self.agents[type_] = []
901 for jid_ in gajim.connections[acct].available_transports[type_]:
902 if not jid_ in self.agents[type_]:
903 self.agents[type_].append(jid_)
904 self.available_types.append(type_)
905 # Combobox with transport/jabber icons
906 liststore = gtk.ListStore(str, gtk.gdk.Pixbuf, str)
907 cell = gtk.CellRendererPixbuf()
908 self.protocol_combobox.pack_start(cell, False)
909 self.protocol_combobox.add_attribute(cell, 'pixbuf', 1)
910 cell = gtk.CellRendererText()
911 cell.set_property('xpad', 5)
912 self.protocol_combobox.pack_start(cell, True)
913 self.protocol_combobox.add_attribute(cell, 'text', 0)
914 self.protocol_combobox.set_model(liststore)
915 uf_type = {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
916 'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
917 # Jabber as first
918 img = gajim.interface.jabber_state_images['16']['online']
919 liststore.append(['Jabber', img.get_pixbuf(), 'jabber'])
920 for type_ in self.agents:
921 if type_ == 'jabber':
922 continue
923 imgs = gajim.interface.roster.transports_state_images
924 img = None
925 if type_ in imgs['16'] and 'online' in imgs['16'][type_]:
926 img = imgs['16'][type_]['online']
927 if type_ in uf_type:
928 liststore.append([uf_type[type_], img.get_pixbuf(), type_])
929 else:
930 liststore.append([type_, img.get_pixbuf(), type_])
931 else:
932 liststore.append([type_, img, type_])
933 self.protocol_combobox.set_active(0)
934 self.auto_authorize_checkbutton.show()
935 liststore = gtk.ListStore(str)
936 self.protocol_jid_combobox.set_model(liststore)
937 if jid:
938 type_ = gajim.get_transport_name_from_jid(jid)
939 if not type_:
940 type_ = 'jabber'
941 if type_ == 'jabber':
942 self.uid_entry.set_text(jid)
943 else:
944 uid, transport = gajim.get_name_and_server_from_jid(jid)
945 self.uid_entry.set_text(uid.replace('%', '@', 1))
946 # set protocol_combobox
947 model = self.protocol_combobox.get_model()
948 iter_ = model.get_iter_first()
949 i = 0
950 while iter_:
951 if model[iter_][2] == type_:
952 self.protocol_combobox.set_active(i)
953 break
954 iter_ = model.iter_next(iter_)
955 i += 1
957 # set protocol_jid_combobox
958 self.protocol_jid_combobox.set_active(0)
959 model = self.protocol_jid_combobox.get_model()
960 iter_ = model.get_iter_first()
961 i = 0
962 while iter_:
963 if model[iter_][0] == transport:
964 self.protocol_jid_combobox.set_active(i)
965 break
966 iter_ = model.iter_next(iter_)
967 i += 1
968 if user_nick:
969 self.nickname_entry.set_text(user_nick)
970 self.nickname_entry.grab_focus()
971 else:
972 self.uid_entry.grab_focus()
973 group_names = []
974 for acct in accounts:
975 for g in gajim.groups[acct].keys():
976 if g not in helpers.special_groups and g not in group_names:
977 group_names.append(g)
978 group_names.sort()
979 i = 0
980 for g in group_names:
981 self.group_comboboxentry.append_text(g)
982 if group == g:
983 self.group_comboboxentry.set_active(i)
984 i += 1
986 self.window.show_all()
988 if self.account:
989 self.account_label.hide()
990 self.account_hbox.hide()
991 else:
992 liststore = gtk.ListStore(str, str)
993 for acct in accounts:
994 liststore.append([acct, acct])
995 self.account_combobox.set_model(liststore)
996 self.account_combobox.set_active(0)
998 if self.account:
999 message_buffer = self.message_textview.get_buffer()
1000 message_buffer.set_text(helpers.get_subscription_request_msg(
1001 self.account))
1003 gajim.ged.register_event_handler('presence-received', ged.GUI1,
1004 self._nec_presence_received)
1006 def on_add_new_contact_window_destroy(self, widget):
1007 if self.account:
1008 location = gajim.interface.instances[self.account]
1009 else:
1010 location = gajim.interface.instances
1011 del location['add_contact']
1012 gajim.ged.remove_event_handler('presence-received', ged.GUI1,
1013 self._nec_presence_received)
1015 def on_register_button_clicked(self, widget):
1016 jid = self.protocol_jid_combobox.get_active_text().decode('utf-8')
1017 gajim.connections[self.account].request_register_agent_info(jid)
1019 def on_add_new_contact_window_key_press_event(self, widget, event):
1020 if event.keyval == gtk.keysyms.Escape: # ESCAPE
1021 self.window.destroy()
1023 def on_cancel_button_clicked(self, widget):
1025 When Cancel button is clicked
1027 self.window.destroy()
1029 def on_add_button_clicked(self, widget):
1031 When Subscribe button is clicked
1033 jid = self.uid_entry.get_text().decode('utf-8').strip()
1034 if not jid:
1035 return
1037 model = self.protocol_combobox.get_model()
1038 iter_ = self.protocol_combobox.get_active_iter()
1039 type_ = model[iter_][2]
1040 if type_ != 'jabber':
1041 transport = self.protocol_jid_combobox.get_active_text().decode(
1042 'utf-8')
1043 jid = jid.replace('@', '%') + '@' + transport
1045 # check if jid is conform to RFC and stringprep it
1046 try:
1047 jid = helpers.parse_jid(jid)
1048 except helpers.InvalidFormat, s:
1049 pritext = _('Invalid User ID')
1050 ErrorDialog(pritext, str(s))
1051 return
1053 # No resource in jid
1054 if jid.find('/') >= 0:
1055 pritext = _('Invalid User ID')
1056 ErrorDialog(pritext, _('The user ID must not contain a resource.'))
1057 return
1059 if jid == gajim.get_jid_from_account(self.account):
1060 pritext = _('Invalid User ID')
1061 ErrorDialog(pritext, _('You cannot add yourself to your roster.'))
1062 return
1064 nickname = self.nickname_entry.get_text().decode('utf-8') or ''
1065 # get value of account combobox, if account was not specified
1066 if not self.account:
1067 model = self.account_combobox.get_model()
1068 index = self.account_combobox.get_active()
1069 self.account = model[index][1]
1071 # Check if jid is already in roster
1072 if jid in gajim.contacts.get_jid_list(self.account):
1073 c = gajim.contacts.get_first_contact_from_jid(self.account, jid)
1074 if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
1075 ErrorDialog(_('Contact already in roster'),
1076 _('This contact is already listed in your roster.'))
1077 return
1079 if type_ == 'jabber':
1080 message_buffer = self.message_textview.get_buffer()
1081 start_iter = message_buffer.get_start_iter()
1082 end_iter = message_buffer.get_end_iter()
1083 message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
1084 if self.save_message_checkbutton.get_active():
1085 gajim.config.set_per('accounts', self.account,
1086 'subscription_request_msg', message)
1087 else:
1088 message= ''
1089 group = self.group_comboboxentry.child.get_text().decode('utf-8')
1090 groups = []
1091 if group:
1092 groups = [group]
1093 auto_auth = self.auto_authorize_checkbutton.get_active()
1094 gajim.interface.roster.req_sub(self, jid, message, self.account,
1095 groups=groups, nickname=nickname, auto_auth=auto_auth)
1096 self.window.destroy()
1098 def on_account_combobox_changed(self, widget):
1099 model = widget.get_model()
1100 iter_ = widget.get_active_iter()
1101 account = model[iter_][0]
1102 message_buffer = self.message_textview.get_buffer()
1103 message_buffer.set_text(helpers.get_subscription_request_msg(account))
1105 def on_protocol_combobox_changed(self, widget):
1106 model = widget.get_model()
1107 iter_ = widget.get_active_iter()
1108 type_ = model[iter_][2]
1109 model = self.protocol_jid_combobox.get_model()
1110 model.clear()
1111 if len(self.agents[type_]):
1112 for jid_ in self.agents[type_]:
1113 model.append([jid_])
1114 self.protocol_jid_combobox.set_active(0)
1115 if len(self.agents[type_]) > 1:
1116 self.protocol_jid_combobox.show()
1117 else:
1118 self.protocol_jid_combobox.hide()
1119 if type_ in self.uid_labels:
1120 self.uid_label.set_text(self.uid_labels[type_])
1121 else:
1122 self.uid_label.set_text(_('User ID:'))
1123 if type_ == 'jabber':
1124 self.message_scrolledwindow.show()
1125 self.save_message_checkbutton.show()
1126 else:
1127 self.message_scrolledwindow.hide()
1128 self.save_message_checkbutton.hide()
1129 if type_ in self.available_types:
1130 self.register_hbox.show()
1131 self.auto_authorize_checkbutton.hide()
1132 self.connected_label.hide()
1133 self.subscription_table.hide()
1134 self.add_button.set_sensitive(False)
1135 else:
1136 self.register_hbox.hide()
1137 if type_ != 'jabber':
1138 jid = self.protocol_jid_combobox.get_active_text()
1139 contact = gajim.contacts.get_first_contact_from_jid(self.account,
1140 jid)
1141 if contact.show in ('offline', 'error'):
1142 self.subscription_table.hide()
1143 self.connected_label.show()
1144 self.add_button.set_sensitive(False)
1145 self.auto_authorize_checkbutton.hide()
1146 return
1147 self.subscription_table.show()
1148 self.auto_authorize_checkbutton.show()
1149 self.connected_label.hide()
1150 self.add_button.set_sensitive(True)
1152 def transport_signed_in(self, jid):
1153 if self.protocol_jid_combobox.get_active_text() == jid:
1154 self.register_hbox.hide()
1155 self.connected_label.hide()
1156 self.subscription_table.show()
1157 self.auto_authorize_checkbutton.show()
1158 self.add_button.set_sensitive(True)
1160 def transport_signed_out(self, jid):
1161 if self.protocol_jid_combobox.get_active_text() == jid:
1162 self.subscription_table.hide()
1163 self.auto_authorize_checkbutton.hide()
1164 self.connected_label.show()
1165 self.add_button.set_sensitive(False)
1167 def _nec_presence_received(self, obj):
1168 if gajim.jid_is_transport(obj.jid):
1169 if obj.old_show == 0 and obj.new_show > 1:
1170 self.transport_signed_in(obj.jid)
1171 elif obj.old_show > 1 and obj.new_show == 0:
1172 self.transport_signed_out(obj.jid)
1175 class AboutDialog:
1177 Class for about dialog
1180 def __init__(self):
1181 dlg = gtk.AboutDialog()
1182 dlg.set_transient_for(gajim.interface.roster.window)
1183 dlg.set_name('Gajim')
1184 dlg.set_version(gajim.version)
1185 s = u'Copyright © 2003-2010 Gajim Team'
1186 dlg.set_copyright(s)
1187 copying_file_path = self.get_path('COPYING')
1188 if copying_file_path:
1189 text = open(copying_file_path).read()
1190 dlg.set_license(text)
1192 dlg.set_comments('%s\n%s %s\n%s %s' % (_('A GTK+ jabber client'),
1193 _('GTK+ Version:'), self.tuple2str(gtk.gtk_version), \
1194 _('PyGTK Version:'), self.tuple2str(gtk.pygtk_version)))
1195 dlg.set_website('http://www.gajim.org/')
1197 authors_file_path = self.get_path('AUTHORS')
1198 if authors_file_path:
1199 authors = []
1200 authors_file = open(authors_file_path).read()
1201 authors_file = authors_file.split('\n')
1202 for author in authors_file:
1203 if author == 'CURRENT DEVELOPERS:':
1204 authors.append(_('Current Developers:'))
1205 elif author == 'PAST DEVELOPERS:':
1206 authors.append('\n' + _('Past Developers:'))
1207 elif author != '': # Real author line
1208 authors.append(author)
1210 thanks_file_path = self.get_path('THANKS')
1211 if thanks_file_path:
1212 authors.append('\n' + _('THANKS:'))
1214 text = open(thanks_file_path).read()
1215 text_splitted = text.split('\n')
1216 text = '\n'.join(text_splitted[:-2]) # remove one english sentence
1217 # and add it manually as translatable
1218 text += '\n%s\n' % _('Last but not least, we would like to thank all '
1219 'the package maintainers.')
1220 authors.append(text)
1222 dlg.set_authors(authors)
1224 dlg.props.wrap_license = True
1226 pixbuf = gtkgui_helpers.get_icon_pixmap('gajim-about', 128)
1228 dlg.set_logo(pixbuf)
1229 #here you write your name in the form Name FamilyName <someone@somewhere>
1230 dlg.set_translator_credits(_('translator-credits'))
1232 thanks_artists_file_path = self.get_path('THANKS.artists')
1233 if thanks_artists_file_path:
1234 artists_text = open(thanks_artists_file_path).read()
1235 artists = artists_text.split('\n')
1236 dlg.set_artists(artists)
1237 # connect close button to destroy() function
1238 for button in dlg.action_area.get_children():
1239 if button.get_property('label') == gtk.STOCK_CLOSE:
1240 button.connect('clicked', lambda x:dlg.destroy())
1241 dlg.show_all()
1243 def tuple2str(self, tuple_):
1244 str_ = ''
1245 for num in tuple_:
1246 str_ += str(num) + '.'
1247 return str_[0:-1] # remove latest .
1249 def get_path(self, filename):
1251 Where can we find this Credits file?
1253 if os.path.isfile(os.path.join(gajim.defs.docdir, filename)):
1254 return os.path.join(gajim.defs.docdir, filename)
1255 elif os.path.isfile('../' + filename):
1256 return ('../' + filename)
1257 else:
1258 return None
1260 class Dialog(gtk.Dialog):
1261 def __init__(self, parent, title, buttons, default=None,
1262 on_response_ok=None, on_response_cancel=None):
1263 gtk.Dialog.__init__(self, title, parent,
1264 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR)
1266 self.user_response_ok = on_response_ok
1267 self.user_response_cancel = on_response_cancel
1268 self.set_border_width(6)
1269 self.vbox.set_spacing(12)
1270 self.set_resizable(False)
1272 possible_responses = {gtk.STOCK_OK: self.on_response_ok,
1273 gtk.STOCK_CANCEL: self.on_response_cancel}
1274 for stock, response in buttons:
1275 b = self.add_button(stock, response)
1276 for response in possible_responses:
1277 if stock == response:
1278 b.connect('clicked', possible_responses[response])
1279 break
1281 if default is not None:
1282 self.set_default_response(default)
1283 else:
1284 self.set_default_response(buttons[-1][1])
1286 def on_response_ok(self, widget):
1287 if self.user_response_ok:
1288 if isinstance(self.user_response_ok, tuple):
1289 self.user_response_ok[0](*self.user_response_ok[1:])
1290 else:
1291 self.user_response_ok()
1292 self.destroy()
1294 def on_response_cancel(self, widget):
1295 if self.user_response_cancel:
1296 if isinstance(self.user_response_cancel, tuple):
1297 self.user_response_cancel[0](*self.user_response_ok[1:])
1298 else:
1299 self.user_response_cancel()
1300 self.destroy()
1302 def just_destroy(self, widget):
1303 self.destroy()
1305 def get_button(self, index):
1306 buttons = self.action_area.get_children()
1307 return index < len(buttons) and buttons[index] or None
1310 class HigDialog(gtk.MessageDialog):
1311 def __init__(self, parent, type_, buttons, pritext, sectext,
1312 on_response_ok=None, on_response_cancel=None, on_response_yes=None,
1313 on_response_no=None):
1314 self.call_cancel_on_destroy = True
1315 gtk.MessageDialog.__init__(self, parent,
1316 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1317 type_, buttons, message_format = pritext)
1319 self.format_secondary_markup(sectext)
1321 buttons = self.action_area.get_children()
1322 self.possible_responses = {gtk.STOCK_OK: on_response_ok,
1323 gtk.STOCK_CANCEL: on_response_cancel, gtk.STOCK_YES: on_response_yes,
1324 gtk.STOCK_NO: on_response_no}
1325 for b in buttons:
1326 for response in self.possible_responses:
1327 if b.get_label() == response:
1328 if not self.possible_responses[response]:
1329 b.connect('clicked', self.just_destroy)
1330 elif isinstance(self.possible_responses[response], tuple):
1331 if len(self.possible_responses[response]) == 1:
1332 b.connect('clicked', self.possible_responses[response][0])
1333 else:
1334 b.connect('clicked', *self.possible_responses[response])
1335 else:
1336 b.connect('clicked', self.possible_responses[response])
1337 break
1339 self.connect('destroy', self.on_dialog_destroy)
1341 def on_dialog_destroy(self, widget):
1342 if not self.call_cancel_on_destroy:
1343 return
1344 cancel_handler = self.possible_responses[gtk.STOCK_CANCEL]
1345 if not cancel_handler:
1346 return False
1347 if isinstance(cancel_handler, tuple):
1348 cancel_handler[0](None, *cancel_handler[1:])
1349 else:
1350 cancel_handler(None)
1352 def just_destroy(self, widget):
1353 self.destroy()
1355 def popup(self):
1357 Show dialog
1359 vb = self.get_children()[0].get_children()[0] # Give focus to top vbox
1360 vb.set_flags(gtk.CAN_FOCUS)
1361 vb.grab_focus()
1362 self.show_all()
1364 class FileChooserDialog(gtk.FileChooserDialog):
1366 Non-blocking FileChooser Dialog around gtk.FileChooserDialog
1368 def __init__(self, title_text, action, buttons, default_response,
1369 select_multiple=False, current_folder=None, on_response_ok=None,
1370 on_response_cancel=None):
1372 gtk.FileChooserDialog.__init__(self, title=title_text, action=action,
1373 buttons=buttons)
1375 self.set_default_response(default_response)
1376 self.set_select_multiple(select_multiple)
1377 if current_folder and os.path.isdir(current_folder):
1378 self.set_current_folder(current_folder)
1379 else:
1380 self.set_current_folder(helpers.get_documents_path())
1381 self.response_ok, self.response_cancel = \
1382 on_response_ok, on_response_cancel
1383 # in gtk+-2.10 clicked signal on some of the buttons in a dialog
1384 # is emitted twice, so we cannot rely on 'clicked' signal
1385 self.connect('response', self.on_dialog_response)
1386 self.show_all()
1388 def on_dialog_response(self, dialog, response):
1389 if response in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_CLOSE):
1390 if self.response_cancel:
1391 if isinstance(self.response_cancel, tuple):
1392 self.response_cancel[0](dialog, *self.response_cancel[1:])
1393 else:
1394 self.response_cancel(dialog)
1395 else:
1396 self.just_destroy(dialog)
1397 elif response == gtk.RESPONSE_OK:
1398 if self.response_ok:
1399 if isinstance(self.response_ok, tuple):
1400 self.response_ok[0](dialog, *self.response_ok[1:])
1401 else:
1402 self.response_ok(dialog)
1403 else:
1404 self.just_destroy(dialog)
1406 def just_destroy(self, widget):
1407 self.destroy()
1409 class AspellDictError:
1410 def __init__(self, lang):
1411 ErrorDialog(
1412 _('Dictionary for lang %s not available') % lang,
1413 _('You have to install %s dictionary to use spellchecking, or '
1414 'choose another language by setting the speller_language option.'
1415 '\n\nHighlighting misspelled words feature will not be used') % lang)
1416 gajim.config.set('use_speller', False)
1418 class ConfirmationDialog(HigDialog):
1420 HIG compliant confirmation dialog
1423 def __init__(self, pritext, sectext='', on_response_ok=None,
1424 on_response_cancel=None):
1425 self.user_response_ok = on_response_ok
1426 self.user_response_cancel = on_response_cancel
1427 HigDialog.__init__(self, None,
1428 gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
1429 self.on_response_ok, self.on_response_cancel)
1430 self.popup()
1432 def on_response_ok(self, widget):
1433 if self.user_response_ok:
1434 if isinstance(self.user_response_ok, tuple):
1435 self.user_response_ok[0](*self.user_response_ok[1:])
1436 else:
1437 self.user_response_ok()
1438 self.call_cancel_on_destroy = False
1439 self.destroy()
1441 def on_response_cancel(self, widget):
1442 if self.user_response_cancel:
1443 if isinstance(self.user_response_cancel, tuple):
1444 self.user_response_cancel[0](*self.user_response_ok[1:])
1445 else:
1446 self.user_response_cancel()
1447 self.call_cancel_on_destroy = False
1448 self.destroy()
1450 class NonModalConfirmationDialog(HigDialog):
1452 HIG compliant non modal confirmation dialog
1455 def __init__(self, pritext, sectext='', on_response_ok=None,
1456 on_response_cancel=None):
1457 self.user_response_ok = on_response_ok
1458 self.user_response_cancel = on_response_cancel
1459 HigDialog.__init__(self, None,
1460 gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
1461 self.on_response_ok, self.on_response_cancel)
1462 self.set_modal(False)
1464 def on_response_ok(self, widget):
1465 if self.user_response_ok:
1466 if isinstance(self.user_response_ok, tuple):
1467 self.user_response_ok[0](*self.user_response_ok[1:])
1468 else:
1469 self.user_response_ok()
1470 self.call_cancel_on_destroy = False
1471 self.destroy()
1473 def on_response_cancel(self, widget):
1474 if self.user_response_cancel:
1475 if isinstance(self.user_response_cancel, tuple):
1476 self.user_response_cancel[0](*self.user_response_cancel[1:])
1477 else:
1478 self.user_response_cancel()
1479 self.call_cancel_on_destroy = False
1480 self.destroy()
1482 class WarningDialog(HigDialog):
1484 HIG compliant warning dialog
1487 def __init__(self, pritext, sectext='', transient_for=None):
1488 HigDialog.__init__(self, None, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
1489 pritext, sectext)
1490 self.set_modal(False)
1491 if transient_for is None and hasattr(gajim.interface, 'roster') and \
1492 gajim.interface.roster:
1493 transient_for = gajim.interface.roster.window
1494 if transient_for:
1495 self.set_transient_for(transient_for)
1496 self.popup()
1498 class InformationDialog(HigDialog):
1500 HIG compliant info dialog
1503 def __init__(self, pritext, sectext=''):
1504 HigDialog.__init__(self, None, gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
1505 pritext, sectext)
1506 self.set_modal(False)
1507 self.set_transient_for(gajim.interface.roster.window)
1508 self.popup()
1510 class ErrorDialog(HigDialog):
1512 HIG compliant error dialog
1515 def __init__(self, pritext, sectext='', on_response_ok=None,
1516 on_response_cancel=None):
1517 HigDialog.__init__( self, None, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
1518 pritext, sectext, on_response_ok=on_response_ok,
1519 on_response_cancel=on_response_cancel)
1520 self.popup()
1522 class YesNoDialog(HigDialog):
1524 HIG compliant YesNo dialog
1527 def __init__(self, pritext, sectext='', checktext='', on_response_yes=None,
1528 on_response_no=None):
1529 self.user_response_yes = on_response_yes
1530 self.user_response_no = on_response_no
1531 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
1532 pritext, sectext, on_response_yes=self.on_response_yes,
1533 on_response_no=self.on_response_no)
1535 if checktext:
1536 self.checkbutton = gtk.CheckButton(checktext)
1537 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1538 else:
1539 self.checkbutton = None
1540 self.set_modal(False)
1541 self.popup()
1543 def on_response_yes(self, widget):
1544 if self.user_response_yes:
1545 if isinstance(self.user_response_yes, tuple):
1546 self.user_response_yes[0](self.is_checked(),
1547 *self.user_response_yes[1:])
1548 else:
1549 self.user_response_yes(self.is_checked())
1550 self.call_cancel_on_destroy = False
1551 self.destroy()
1553 def on_response_no(self, widget):
1554 if self.user_response_no:
1555 if isinstance(self.user_response_no, tuple):
1556 self.user_response_no[0](*self.user_response_no[1:])
1557 else:
1558 self.user_response_no()
1559 self.call_cancel_on_destroy = False
1560 self.destroy()
1562 def is_checked(self):
1564 Get active state of the checkbutton
1566 if not self.checkbutton:
1567 return False
1568 return self.checkbutton.get_active()
1570 class ConfirmationDialogCheck(ConfirmationDialog):
1572 HIG compliant confirmation dialog with checkbutton
1575 def __init__(self, pritext, sectext='', checktext='', on_response_ok=None,
1576 on_response_cancel=None, is_modal=True):
1577 self.user_response_ok = on_response_ok
1578 self.user_response_cancel = on_response_cancel
1580 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1581 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1582 self.on_response_cancel)
1584 self.set_default_response(gtk.RESPONSE_OK)
1586 ok_button = self.action_area.get_children()[0] # right to left
1587 ok_button.grab_focus()
1589 self.checkbutton = gtk.CheckButton(checktext)
1590 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1591 self.set_modal(is_modal)
1592 self.popup()
1594 def on_response_ok(self, widget):
1595 if self.user_response_ok:
1596 if isinstance(self.user_response_ok, tuple):
1597 self.user_response_ok[0](self.is_checked(),
1598 *self.user_response_ok[1:])
1599 else:
1600 self.user_response_ok(self.is_checked())
1601 self.call_cancel_on_destroy = False
1602 self.destroy()
1604 def on_response_cancel(self, widget):
1605 if self.user_response_cancel:
1606 if isinstance(self.user_response_cancel, tuple):
1607 self.user_response_cancel[0](self.is_checked(),
1608 *self.user_response_cancel[1:])
1609 else:
1610 self.user_response_cancel(self.is_checked())
1611 self.call_cancel_on_destroy = False
1612 self.destroy()
1614 def is_checked(self):
1616 Get active state of the checkbutton
1618 return self.checkbutton.get_active()
1620 class ConfirmationDialogDoubleCheck(ConfirmationDialog):
1622 HIG compliant confirmation dialog with 2 checkbuttons
1625 def __init__(self, pritext, sectext='', checktext1='', checktext2='',
1626 on_response_ok=None, on_response_cancel=None, is_modal=True):
1627 self.user_response_ok = on_response_ok
1628 self.user_response_cancel = on_response_cancel
1630 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1631 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1632 self.on_response_cancel)
1634 self.set_default_response(gtk.RESPONSE_OK)
1636 ok_button = self.action_area.get_children()[0] # right to left
1637 ok_button.grab_focus()
1639 if checktext1:
1640 self.checkbutton1 = gtk.CheckButton(checktext1)
1641 self.vbox.pack_start(self.checkbutton1, expand=False, fill=True)
1642 else:
1643 self.checkbutton1 = None
1644 if checktext2:
1645 self.checkbutton2 = gtk.CheckButton(checktext2)
1646 self.vbox.pack_start(self.checkbutton2, expand=False, fill=True)
1647 else:
1648 self.checkbutton2 = None
1650 self.set_modal(is_modal)
1651 self.popup()
1653 def on_response_ok(self, widget):
1654 if self.user_response_ok:
1655 if isinstance(self.user_response_ok, tuple):
1656 self.user_response_ok[0](self.is_checked(),
1657 *self.user_response_ok[1:])
1658 else:
1659 self.user_response_ok(self.is_checked())
1660 self.call_cancel_on_destroy = False
1661 self.destroy()
1663 def on_response_cancel(self, widget):
1664 if self.user_response_cancel:
1665 if isinstance(self.user_response_cancel, tuple):
1666 self.user_response_cancel[0](*self.user_response_cancel[1:])
1667 else:
1668 self.user_response_cancel()
1669 self.call_cancel_on_destroy = False
1670 self.destroy()
1672 def is_checked(self):
1673 ''' Get active state of the checkbutton '''
1674 if self.checkbutton1:
1675 is_checked_1 = self.checkbutton1.get_active()
1676 else:
1677 is_checked_1 = False
1678 if self.checkbutton2:
1679 is_checked_2 = self.checkbutton2.get_active()
1680 else:
1681 is_checked_2 = False
1682 return [is_checked_1, is_checked_2]
1684 class ConfirmationDialogDoubleRadio(ConfirmationDialog):
1686 HIG compliant confirmation dialog with 2 radios
1689 def __init__(self, pritext, sectext='', radiotext1='', radiotext2='',
1690 on_response_ok=None, on_response_cancel=None, is_modal=True):
1691 self.user_response_ok = on_response_ok
1692 self.user_response_cancel = on_response_cancel
1694 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1695 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1696 self.on_response_cancel)
1698 self.set_default_response(gtk.RESPONSE_OK)
1700 ok_button = self.action_area.get_children()[0] # right to left
1701 ok_button.grab_focus()
1703 self.radiobutton1 = gtk.RadioButton(label=radiotext1)
1704 self.vbox.pack_start(self.radiobutton1, expand=False, fill=True)
1706 self.radiobutton2 = gtk.RadioButton(group=self.radiobutton1,
1707 label=radiotext2)
1708 self.vbox.pack_start(self.radiobutton2, expand=False, fill=True)
1710 self.set_modal(is_modal)
1711 self.popup()
1713 def on_response_ok(self, widget):
1714 if self.user_response_ok:
1715 if isinstance(self.user_response_ok, tuple):
1716 self.user_response_ok[0](self.is_checked(),
1717 *self.user_response_ok[1:])
1718 else:
1719 self.user_response_ok(self.is_checked())
1720 self.call_cancel_on_destroy = False
1721 self.destroy()
1723 def on_response_cancel(self, widget):
1724 if self.user_response_cancel:
1725 if isinstance(self.user_response_cancel, tuple):
1726 self.user_response_cancel[0](*self.user_response_cancel[1:])
1727 else:
1728 self.user_response_cancel()
1729 self.call_cancel_on_destroy = False
1730 self.destroy()
1732 def is_checked(self):
1733 ''' Get active state of the checkbutton '''
1734 if self.radiobutton1:
1735 is_checked_1 = self.radiobutton1.get_active()
1736 else:
1737 is_checked_1 = False
1738 if self.radiobutton2:
1739 is_checked_2 = self.radiobutton2.get_active()
1740 else:
1741 is_checked_2 = False
1742 return [is_checked_1, is_checked_2]
1744 class FTOverwriteConfirmationDialog(ConfirmationDialog):
1746 HIG compliant confirmation dialog to overwrite or resume a file transfert
1749 def __init__(self, pritext, sectext='', propose_resume=True,
1750 on_response=None):
1751 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_CANCEL,
1752 pritext, sectext)
1754 self.on_response = on_response
1756 if propose_resume:
1757 b = gtk.Button('', gtk.STOCK_REFRESH)
1758 align = b.get_children()[0]
1759 hbox = align.get_children()[0]
1760 label = hbox.get_children()[1]
1761 label.set_text('_Resume')
1762 label.set_use_underline(True)
1763 self.add_action_widget(b, 100)
1765 b = gtk.Button('', gtk.STOCK_SAVE_AS)
1766 align = b.get_children()[0]
1767 hbox = align.get_children()[0]
1768 label = hbox.get_children()[1]
1769 label.set_text('Re_place')
1770 label.set_use_underline(True)
1771 self.add_action_widget(b, 200)
1773 self.connect('response', self.on_dialog_response)
1774 self.show_all()
1776 def on_dialog_response(self, dialog, response):
1777 if self.on_response:
1778 if isinstance(self.on_response, tuple):
1779 self.on_response[0](response, *self.on_response[1:])
1780 else:
1781 self.on_response(response)
1782 self.call_cancel_on_destroy = False
1783 self.destroy()
1785 class CommonInputDialog:
1787 Common Class for Input dialogs
1790 def __init__(self, title, label_str, is_modal, ok_handler, cancel_handler):
1791 self.dialog = self.xml.get_object('input_dialog')
1792 label = self.xml.get_object('label')
1793 self.dialog.set_title(title)
1794 label.set_markup(label_str)
1795 self.cancel_handler = cancel_handler
1796 self.vbox = self.xml.get_object('vbox')
1798 self.ok_handler = ok_handler
1799 okbutton = self.xml.get_object('okbutton')
1800 okbutton.connect('clicked', self.on_okbutton_clicked)
1801 cancelbutton = self.xml.get_object('cancelbutton')
1802 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
1803 self.xml.connect_signals(self)
1804 self.dialog.show_all()
1806 def on_input_dialog_destroy(self, widget):
1807 if self.cancel_handler:
1808 self.cancel_handler()
1810 def on_okbutton_clicked(self, widget):
1811 user_input = self.get_text()
1812 if user_input:
1813 user_input = user_input.decode('utf-8')
1814 self.cancel_handler = None
1815 self.dialog.destroy()
1816 if isinstance(self.ok_handler, tuple):
1817 self.ok_handler[0](user_input, *self.ok_handler[1:])
1818 else:
1819 self.ok_handler(user_input)
1821 def on_cancelbutton_clicked(self, widget):
1822 self.dialog.destroy()
1824 class InputDialog(CommonInputDialog):
1826 Class for Input dialog
1829 def __init__(self, title, label_str, input_str=None, is_modal=True,
1830 ok_handler=None, cancel_handler=None):
1831 self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
1832 CommonInputDialog.__init__(self, title, label_str, is_modal, ok_handler,
1833 cancel_handler)
1834 self.input_entry = self.xml.get_object('input_entry')
1835 if input_str:
1836 self.set_entry(input_str)
1838 def set_entry(self, value):
1839 self.input_entry.set_text(value)
1840 self.input_entry.select_region(0, -1) # select all
1842 def get_text(self):
1843 return self.input_entry.get_text().decode('utf-8')
1845 class InputDialogCheck(InputDialog):
1847 Class for Input dialog
1850 def __init__(self, title, label_str, checktext='', input_str=None,
1851 is_modal=True, ok_handler=None, cancel_handler=None):
1852 self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
1853 InputDialog.__init__(self, title, label_str, input_str=input_str,
1854 is_modal=is_modal, ok_handler=ok_handler,
1855 cancel_handler=cancel_handler)
1856 self.input_entry = self.xml.get_object('input_entry')
1857 if input_str:
1858 self.input_entry.set_text(input_str)
1859 self.input_entry.select_region(0, -1) # select all
1861 if checktext:
1862 self.checkbutton = gtk.CheckButton(checktext)
1863 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1864 self.checkbutton.show()
1866 def on_okbutton_clicked(self, widget):
1867 user_input = self.get_text()
1868 if user_input:
1869 user_input = user_input.decode('utf-8')
1870 self.cancel_handler = None
1871 self.dialog.destroy()
1872 if isinstance(self.ok_handler, tuple):
1873 self.ok_handler[0](user_input, self.is_checked(), *self.ok_handler[1:])
1874 else:
1875 self.ok_handler(user_input, self.is_checked())
1877 def get_text(self):
1878 return self.input_entry.get_text().decode('utf-8')
1880 def is_checked(self):
1882 Get active state of the checkbutton
1884 try:
1885 return self.checkbutton.get_active()
1886 except Exception:
1887 # There is no checkbutton
1888 return False
1890 class ChangeNickDialog(InputDialogCheck):
1892 Class for changing room nickname in case of conflict
1895 def __init__(self, account, room_jid, title, prompt, check_text=None,
1896 change_nick=False):
1898 change_nick must be set to True when we are already occupant of the room
1899 and we are changing our nick
1901 InputDialogCheck.__init__(self, title, '', checktext=check_text,
1902 input_str='', is_modal=True, ok_handler=None, cancel_handler=None)
1903 self.room_queue = [(account, room_jid, prompt, change_nick)]
1904 self.check_next()
1906 def on_input_dialog_delete_event(self, widget, event):
1907 self.on_cancelbutton_clicked(widget)
1908 return True
1910 def setup_dialog(self):
1911 self.gc_control = gajim.interface.msg_win_mgr.get_gc_control(
1912 self.room_jid, self.account)
1913 if not self.gc_control and \
1914 self.room_jid in gajim.interface.minimized_controls[self.account]:
1915 self.gc_control = \
1916 gajim.interface.minimized_controls[self.account][self.room_jid]
1917 if not self.gc_control:
1918 self.check_next()
1919 return
1920 label = self.xml.get_object('label')
1921 label.set_markup(self.prompt)
1922 self.set_entry(self.gc_control.nick + \
1923 gajim.config.get('gc_proposed_nick_char'))
1925 def check_next(self):
1926 if len(self.room_queue) == 0:
1927 self.cancel_handler = None
1928 self.dialog.destroy()
1929 if 'change_nick_dialog' in gajim.interface.instances:
1930 del gajim.interface.instances['change_nick_dialog']
1931 return
1932 self.account, self.room_jid, self.prompt, self.change_nick = \
1933 self.room_queue.pop(0)
1934 self.setup_dialog()
1936 if gajim.new_room_nick is not None and not gajim.gc_connected[
1937 self.account][self.room_jid] and self.gc_control.nick != \
1938 gajim.new_room_nick:
1939 self.dialog.hide()
1940 self.on_ok(gajim.new_room_nick, True)
1941 else:
1942 self.dialog.show()
1944 def on_okbutton_clicked(self, widget):
1945 nick = self.get_text()
1946 if nick:
1947 nick = nick.decode('utf-8')
1948 # send presence to room
1949 try:
1950 nick = helpers.parse_resource(nick)
1951 except Exception:
1952 # invalid char
1953 dialogs.ErrorDialog(_('Invalid nickname'),
1954 _('The nickname has not allowed characters.'))
1955 return
1956 self.on_ok(nick, self.is_checked())
1958 def on_ok(self, nick, is_checked):
1959 if is_checked:
1960 gajim.new_room_nick = nick
1961 gajim.connections[self.account].join_gc(nick, self.room_jid, None,
1962 change_nick=self.change_nick)
1963 if gajim.gc_connected[self.account][self.room_jid]:
1964 # We are changing nick, we will change self.nick when we receive
1965 # presence that inform that it works
1966 self.gc_control.new_nick = nick
1967 else:
1968 # We are connecting, we will not get a changed nick presence so
1969 # change it NOW. We don't already have a nick so it's harmless
1970 self.gc_control.nick = nick
1971 self.check_next()
1973 def on_cancelbutton_clicked(self, widget):
1974 self.gc_control.new_nick = ''
1975 self.check_next()
1977 def add_room(self, account, room_jid, prompt, change_nick=False):
1978 if (account, room_jid, prompt, change_nick) not in self.room_queue:
1979 self.room_queue.append((account, room_jid, prompt, change_nick))
1981 class InputTextDialog(CommonInputDialog):
1983 Class for multilines Input dialog (more place than InputDialog)
1986 def __init__(self, title, label_str, input_str=None, is_modal=True,
1987 ok_handler=None, cancel_handler=None):
1988 self.xml = gtkgui_helpers.get_gtk_builder('input_text_dialog.ui')
1989 CommonInputDialog.__init__(self, title, label_str, is_modal, ok_handler,
1990 cancel_handler)
1991 self.input_buffer = self.xml.get_object('input_textview').get_buffer()
1992 if input_str:
1993 self.input_buffer.set_text(input_str)
1994 start_iter, end_iter = self.input_buffer.get_bounds()
1995 self.input_buffer.select_range(start_iter, end_iter) # select all
1997 def get_text(self):
1998 start_iter, end_iter = self.input_buffer.get_bounds()
1999 return self.input_buffer.get_text(start_iter, end_iter).decode('utf-8')
2001 class DoubleInputDialog:
2003 Class for Double Input dialog
2006 def __init__(self, title, label_str1, label_str2, input_str1=None,
2007 input_str2=None, is_modal=True, ok_handler=None, cancel_handler=None):
2008 self.xml = gtkgui_helpers.get_gtk_builder('dubbleinput_dialog.ui')
2009 self.dialog = self.xml.get_object('dubbleinput_dialog')
2010 label1 = self.xml.get_object('label1')
2011 self.input_entry1 = self.xml.get_object('input_entry1')
2012 label2 = self.xml.get_object('label2')
2013 self.input_entry2 = self.xml.get_object('input_entry2')
2014 self.dialog.set_title(title)
2015 label1.set_markup(label_str1)
2016 label2.set_markup(label_str2)
2017 self.cancel_handler = cancel_handler
2018 if input_str1:
2019 self.input_entry1.set_text(input_str1)
2020 self.input_entry1.select_region(0, -1) # select all
2021 if input_str2:
2022 self.input_entry2.set_text(input_str2)
2023 self.input_entry2.select_region(0, -1) # select all
2025 self.dialog.set_modal(is_modal)
2027 self.ok_handler = ok_handler
2028 okbutton = self.xml.get_object('okbutton')
2029 okbutton.connect('clicked', self.on_okbutton_clicked)
2030 cancelbutton = self.xml.get_object('cancelbutton')
2031 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
2032 self.xml.connect_signals(self)
2033 self.dialog.show_all()
2035 def on_dubbleinput_dialog_destroy(self, widget):
2036 if not self.cancel_handler:
2037 return False
2038 if isinstance(self.cancel_handler, tuple):
2039 self.cancel_handler[0](*self.cancel_handler[1:])
2040 else:
2041 self.cancel_handler()
2043 def on_okbutton_clicked(self, widget):
2044 user_input1 = self.input_entry1.get_text().decode('utf-8')
2045 user_input2 = self.input_entry2.get_text().decode('utf-8')
2046 self.dialog.destroy()
2047 if not self.ok_handler:
2048 return
2049 if isinstance(self.ok_handler, tuple):
2050 self.ok_handler[0](user_input1, user_input2, *self.ok_handler[1:])
2051 else:
2052 self.ok_handler(user_input1, user_input2)
2054 def on_cancelbutton_clicked(self, widget):
2055 self.dialog.destroy()
2056 if not self.cancel_handler:
2057 return
2058 if isinstance(self.cancel_handler, tuple):
2059 self.cancel_handler[0](*self.cancel_handler[1:])
2060 else:
2061 self.cancel_handler()
2063 class SubscriptionRequestWindow:
2064 def __init__(self, jid, text, account, user_nick=None):
2065 xml = gtkgui_helpers.get_gtk_builder('subscription_request_window.ui')
2066 self.window = xml.get_object('subscription_request_window')
2067 self.jid = jid
2068 self.account = account
2069 self.user_nick = user_nick
2070 if len(gajim.connections) >= 2:
2071 prompt_text = \
2072 _('Subscription request for account %(account)s from %(jid)s')\
2073 % {'account': account, 'jid': self.jid}
2074 else:
2075 prompt_text = _('Subscription request from %s') % self.jid
2076 xml.get_object('from_label').set_text(prompt_text)
2077 xml.get_object('message_textview').get_buffer().set_text(text)
2078 xml.connect_signals(self)
2079 self.window.show_all()
2081 def prepare_popup_menu(self):
2082 xml = gtkgui_helpers.get_gtk_builder('subscription_request_popup_menu.ui')
2083 menu = xml.get_object('subscription_request_popup_menu')
2084 xml.connect_signals(self)
2085 return menu
2087 def on_close_button_clicked(self, widget):
2088 self.window.destroy()
2090 def on_authorize_button_clicked(self, widget):
2092 Accept the request
2094 gajim.connections[self.account].send_authorization(self.jid)
2095 self.window.destroy()
2096 contact = gajim.contacts.get_contact(self.account, self.jid)
2097 if not contact or _('Not in Roster') in contact.groups:
2098 AddNewContactWindow(self.account, self.jid, self.user_nick)
2100 def on_contact_info_activate(self, widget):
2102 Ask vcard
2104 if self.jid in gajim.interface.instances[self.account]['infos']:
2105 gajim.interface.instances[self.account]['infos'][self.jid].window.present()
2106 else:
2107 contact = gajim.contacts.create_contact(jid=self.jid, account=self.account)
2108 gajim.interface.instances[self.account]['infos'][self.jid] = \
2109 vcard.VcardWindow(contact, self.account)
2110 # Remove jabber page
2111 gajim.interface.instances[self.account]['infos'][self.jid].xml.\
2112 get_object('information_notebook').remove_page(0)
2114 def on_start_chat_activate(self, widget):
2116 Open chat
2118 gajim.interface.new_chat_from_jid(self.account, self.jid)
2120 def on_deny_button_clicked(self, widget):
2122 Refuse the request
2124 gajim.connections[self.account].refuse_authorization(self.jid)
2125 contact = gajim.contacts.get_contact(self.account, self.jid)
2126 if contact and _('Not in Roster') in contact.get_shown_groups():
2127 gajim.interface.roster.remove_contact(self.jid, self.account)
2128 self.window.destroy()
2130 def on_actions_button_clicked(self, widget):
2132 Popup action menu
2134 menu = self.prepare_popup_menu()
2135 menu.show_all()
2136 gtkgui_helpers.popup_emoticons_under_button(menu, widget,
2137 self.window.window)
2140 class JoinGroupchatWindow:
2141 def __init__(self, account=None, room_jid='', nick='', password='',
2142 automatic=False):
2144 Automatic is a dict like {'invities': []}. If automatic is not empty,
2145 this means room must be automaticaly configured and when done, invities
2146 must be automatically invited
2148 if account:
2149 if room_jid != '' and room_jid in gajim.gc_connected[account] and\
2150 gajim.gc_connected[account][room_jid]:
2151 ErrorDialog(_('You are already in group chat %s') % room_jid)
2152 raise GajimGeneralException, 'You are already in this group chat'
2153 if nick == '':
2154 nick = gajim.nicks[account]
2155 if gajim.connections[account].connected < 2:
2156 ErrorDialog(_('You are not connected to the server'),
2157 _('You can not join a group chat unless you are connected.'))
2158 raise GajimGeneralException, 'You must be connected to join a groupchat'
2160 self.xml = gtkgui_helpers.get_gtk_builder('join_groupchat_window.ui')
2162 account_label = self.xml.get_object('account_label')
2163 account_combobox = self.xml.get_object('account_combobox')
2164 account_label.set_no_show_all(False)
2165 account_combobox.set_no_show_all(False)
2166 liststore = gtk.ListStore(str)
2167 account_combobox.set_model(liststore)
2168 cell = gtk.CellRendererText()
2169 account_combobox.pack_start(cell, True)
2170 account_combobox.add_attribute(cell, 'text', 0)
2171 account_combobox.set_active(-1)
2173 # Add accounts, set current as active if it matches 'account'
2174 for acct in [a for a in gajim.connections if \
2175 gajim.account_is_connected(a)]:
2176 account_combobox.append_text(acct)
2177 if account and account == acct:
2178 account_combobox.set_active(liststore.iter_n_children(None)-1)
2180 self.account = account
2181 self.automatic = automatic
2182 self._empty_required_widgets = []
2184 self.window = self.xml.get_object('join_groupchat_window')
2185 self._room_jid_entry = self.xml.get_object('room_jid_entry')
2186 self._nickname_entry = self.xml.get_object('nickname_entry')
2187 self._password_entry = self.xml.get_object('password_entry')
2189 self._nickname_entry.set_text(nick)
2190 if password:
2191 self._password_entry.set_text(password)
2192 self.xml.connect_signals(self)
2193 title = None
2194 if account:
2195 # now add us to open windows
2196 gajim.interface.instances[account]['join_gc'] = self
2197 if len(gajim.connections) > 1:
2198 title = _('Join Group Chat with account %s') % account
2199 if title is None:
2200 title = _('Join Group Chat')
2201 self.window.set_title(title)
2203 self.server_comboboxentry = self.xml.get_object('server_comboboxentry')
2204 self.server_model = self.server_comboboxentry.get_model()
2205 server_list = []
2206 # get the muc server of our server
2207 if 'jabber' in gajim.connections[account].muc_jid:
2208 server_list.append(gajim.connections[account].muc_jid['jabber'])
2210 self.recently_combobox = self.xml.get_object('recently_combobox')
2211 liststore = gtk.ListStore(str)
2212 self.recently_combobox.set_model(liststore)
2213 cell = gtk.CellRendererText()
2214 self.recently_combobox.pack_start(cell, True)
2215 self.recently_combobox.add_attribute(cell, 'text', 0)
2216 self.recently_groupchat = gajim.config.get('recently_groupchat').split()
2217 for g in self.recently_groupchat:
2218 self.recently_combobox.append_text(g)
2219 server = gajim.get_server_from_jid(g)
2220 if server not in server_list and not server.startswith('irc'):
2221 server_list.append(server)
2223 for s in server_list:
2224 self.server_model.append([s])
2225 self.server_comboboxentry.set_active(0)
2227 self._set_room_jid(room_jid)
2229 if len(self.recently_groupchat) == 0:
2230 self.recently_combobox.set_sensitive(False)
2231 elif room_jid == '':
2232 self.recently_combobox.set_active(0)
2233 self._room_jid_entry.select_region(0, -1)
2234 elif room_jid != '':
2235 self.xml.get_object('join_button').grab_focus()
2237 if not self._room_jid_entry.get_text():
2238 self._empty_required_widgets.append(self._room_jid_entry)
2239 if not self._nickname_entry.get_text():
2240 self._empty_required_widgets.append(self._nickname_entry)
2241 if len(self._empty_required_widgets):
2242 self.xml.get_object('join_button').set_sensitive(False)
2244 if account and not gajim.connections[account].private_storage_supported:
2245 self.xml.get_object('bookmark_checkbutton').set_sensitive(False)
2247 self.window.show_all()
2249 def on_join_groupchat_window_destroy(self, widget):
2251 Close window
2253 if self.account and 'join_gc' in gajim.interface.instances[self.account]:
2254 # remove us from open windows
2255 del gajim.interface.instances[self.account]['join_gc']
2257 def on_join_groupchat_window_key_press_event(self, widget, event):
2258 if event.keyval == gtk.keysyms.Escape: # ESCAPE
2259 widget.destroy()
2261 def on_required_entry_changed(self, widget):
2262 if not widget.get_text():
2263 self._empty_required_widgets.append(widget)
2264 self.xml.get_object('join_button').set_sensitive(False)
2265 else:
2266 if widget in self._empty_required_widgets:
2267 self._empty_required_widgets.remove(widget)
2268 if not self._empty_required_widgets and self.account:
2269 self.xml.get_object('join_button').set_sensitive(True)
2270 text = self._room_jid_entry.get_text()
2271 if widget == self._room_jid_entry and '@' in text:
2272 # Don't allow @ char in room entry
2273 room_jid, server = text.split('@', 1)
2274 self._room_jid_entry.set_text(room_jid)
2275 if server:
2276 self.server_comboboxentry.child.set_text(server)
2277 self.server_comboboxentry.grab_focus()
2279 def on_account_combobox_changed(self, widget):
2280 model = widget.get_model()
2281 iter_ = widget.get_active_iter()
2282 self.account = model[iter_][0].decode('utf-8')
2283 self.on_required_entry_changed(self._nickname_entry)
2285 def _set_room_jid(self, room_jid):
2286 room, server = gajim.get_name_and_server_from_jid(room_jid)
2287 self._room_jid_entry.set_text(room)
2288 self.server_comboboxentry.child.set_text(server)
2290 def on_recently_combobox_changed(self, widget):
2291 model = widget.get_model()
2292 iter_ = widget.get_active_iter()
2293 room_jid = model[iter_][0].decode('utf-8')
2294 self._set_room_jid(room_jid)
2296 def on_browse_rooms_button_clicked(self, widget):
2297 server = self.server_comboboxentry.child.get_text().decode('utf-8')
2298 if server in gajim.interface.instances[self.account]['disco']:
2299 gajim.interface.instances[self.account]['disco'][server].window.\
2300 present()
2301 else:
2302 try:
2303 # Object will add itself to the window dict
2304 disco.ServiceDiscoveryWindow(self.account, server,
2305 initial_identities=[{'category': 'conference',
2306 'type': 'text'}])
2307 except GajimGeneralException:
2308 pass
2310 def on_cancel_button_clicked(self, widget):
2312 When Cancel button is clicked
2314 self.window.destroy()
2316 def on_bookmark_checkbutton_toggled(self, widget):
2317 auto_join_checkbutton = self.xml.get_object('auto_join_checkbutton')
2318 if widget.get_active():
2319 auto_join_checkbutton.set_sensitive(True)
2320 else:
2321 auto_join_checkbutton.set_sensitive(False)
2323 def on_join_button_clicked(self, widget):
2325 When Join button is clicked
2327 if not self.account:
2328 ErrorDialog(_('Invalid Account'),
2329 _('You have to choose an account from which you want to join the '
2330 'groupchat.'))
2331 return
2332 nickname = self._nickname_entry.get_text().decode('utf-8')
2333 server = self.server_comboboxentry.child.get_text().decode('utf-8')
2334 room = self._room_jid_entry.get_text().decode('utf-8')
2335 room_jid = room + '@' + server
2336 password = self._password_entry.get_text().decode('utf-8')
2337 try:
2338 nickname = helpers.parse_resource(nickname)
2339 except Exception:
2340 ErrorDialog(_('Invalid Nickname'),
2341 _('The nickname has not allowed characters.'))
2342 return
2343 user, server, resource = helpers.decompose_jid(room_jid)
2344 if not user or not server or resource:
2345 ErrorDialog(_('Invalid group chat Jabber ID'),
2346 _('Please enter the group chat Jabber ID as room@server.'))
2347 return
2348 try:
2349 room_jid = helpers.parse_jid(room_jid)
2350 except Exception:
2351 ErrorDialog(_('Invalid group chat Jabber ID'),
2352 _('The group chat Jabber ID has not allowed characters.'))
2353 return
2355 if gajim.interface.msg_win_mgr.has_window(room_jid, self.account):
2356 ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
2357 self.account)
2358 if ctrl.type_id != message_control.TYPE_GC:
2359 ErrorDialog(_('This is not a group chat'),
2360 _('%s is not the name of a group chat.') % room_jid)
2361 return
2362 if room_jid in self.recently_groupchat:
2363 self.recently_groupchat.remove(room_jid)
2364 self.recently_groupchat.insert(0, room_jid)
2365 if len(self.recently_groupchat) > 10:
2366 self.recently_groupchat = self.recently_groupchat[0:10]
2367 gajim.config.set('recently_groupchat',
2368 ' '.join(self.recently_groupchat))
2370 if self.xml.get_object('bookmark_checkbutton').get_active():
2371 if self.xml.get_object('auto_join_checkbutton').get_active():
2372 autojoin = '1'
2373 else:
2374 autojoin = '0'
2375 # Add as bookmark, with autojoin and not minimized
2376 name = gajim.get_nick_from_jid(room_jid)
2377 gajim.interface.add_gc_bookmark(self.account, name, room_jid, autojoin,
2378 '0', password, nickname)
2380 if self.automatic:
2381 gajim.automatic_rooms[self.account][room_jid] = self.automatic
2382 gajim.interface.join_gc_room(self.account, room_jid, nickname, password)
2384 self.window.destroy()
2386 class SynchroniseSelectAccountDialog:
2387 def __init__(self, account):
2388 # 'account' can be None if we are about to create our first one
2389 if not account or gajim.connections[account].connected < 2:
2390 ErrorDialog(_('You are not connected to the server'),
2391 _('Without a connection, you can not synchronise your contacts.'))
2392 raise GajimGeneralException, 'You are not connected to the server'
2393 self.account = account
2394 self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
2395 self.dialog = self.xml.get_object('synchronise_select_account_dialog')
2396 self.accounts_treeview = self.xml.get_object('accounts_treeview')
2397 model = gtk.ListStore(str, str, bool)
2398 self.accounts_treeview.set_model(model)
2399 # columns
2400 renderer = gtk.CellRendererText()
2401 self.accounts_treeview.insert_column_with_attributes(-1, _('Name'),
2402 renderer, text=0)
2403 renderer = gtk.CellRendererText()
2404 self.accounts_treeview.insert_column_with_attributes(-1, _('Server'),
2405 renderer, text=1)
2407 self.xml.connect_signals(self)
2408 self.init_accounts()
2409 self.dialog.show_all()
2411 def on_accounts_window_key_press_event(self, widget, event):
2412 if event.keyval == gtk.keysyms.Escape:
2413 self.window.destroy()
2415 def init_accounts(self):
2417 Initialize listStore with existing accounts
2419 model = self.accounts_treeview.get_model()
2420 model.clear()
2421 for remote_account in gajim.connections:
2422 if remote_account == self.account:
2423 # Do not show the account we're sync'ing
2424 continue
2425 iter_ = model.append()
2426 model.set(iter_, 0, remote_account, 1,
2427 gajim.get_hostname_from_account(remote_account))
2429 def on_cancel_button_clicked(self, widget):
2430 self.dialog.destroy()
2432 def on_ok_button_clicked(self, widget):
2433 sel = self.accounts_treeview.get_selection()
2434 (model, iter_) = sel.get_selected()
2435 if not iter_:
2436 return
2437 remote_account = model.get_value(iter_, 0).decode('utf-8')
2439 if gajim.connections[remote_account].connected < 2:
2440 ErrorDialog(_('This account is not connected to the server'),
2441 _('You cannot synchronize with an account unless it is connected.'))
2442 return
2443 else:
2444 try:
2445 SynchroniseSelectContactsDialog(self.account, remote_account)
2446 except GajimGeneralException:
2447 # if we showed ErrorDialog, there will not be dialog instance
2448 return
2449 self.dialog.destroy()
2451 class SynchroniseSelectContactsDialog:
2452 def __init__(self, account, remote_account):
2453 self.local_account = account
2454 self.remote_account = remote_account
2455 self.xml = gtkgui_helpers.get_gtk_builder(
2456 'synchronise_select_contacts_dialog.ui')
2457 self.dialog = self.xml.get_object('synchronise_select_contacts_dialog')
2458 self.contacts_treeview = self.xml.get_object('contacts_treeview')
2459 model = gtk.ListStore(bool, str)
2460 self.contacts_treeview.set_model(model)
2461 # columns
2462 renderer1 = gtk.CellRendererToggle()
2463 renderer1.set_property('activatable', True)
2464 renderer1.connect('toggled', self.toggled_callback)
2465 self.contacts_treeview.insert_column_with_attributes(-1,
2466 _('Synchronise'), renderer1, active=0)
2467 renderer2 = gtk.CellRendererText()
2468 self.contacts_treeview.insert_column_with_attributes(-1, _('Name'),
2469 renderer2, text=1)
2471 self.xml.connect_signals(self)
2472 self.init_contacts()
2473 self.dialog.show_all()
2475 def toggled_callback(self, cell, path):
2476 model = self.contacts_treeview.get_model()
2477 iter_ = model.get_iter(path)
2478 model[iter_][0] = not cell.get_active()
2480 def on_contacts_window_key_press_event(self, widget, event):
2481 if event.keyval == gtk.keysyms.Escape:
2482 self.window.destroy()
2484 def init_contacts(self):
2486 Initialize listStore with existing accounts
2488 model = self.contacts_treeview.get_model()
2489 model.clear()
2491 # recover local contacts
2492 local_jid_list = gajim.contacts.get_contacts_jid_list(self.local_account)
2494 remote_jid_list = gajim.contacts.get_contacts_jid_list(
2495 self.remote_account)
2496 for remote_jid in remote_jid_list:
2497 if remote_jid not in local_jid_list:
2498 iter_ = model.append()
2499 model.set(iter_, 0, True, 1, remote_jid)
2501 def on_cancel_button_clicked(self, widget):
2502 self.dialog.destroy()
2504 def on_ok_button_clicked(self, widget):
2505 model = self.contacts_treeview.get_model()
2506 iter_ = model.get_iter_root()
2507 while iter_:
2508 if model[iter_][0]:
2509 # it is selected
2510 remote_jid = model[iter_][1].decode('utf-8')
2511 message = 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
2512 gajim.get_hostname_from_account(self.remote_account)
2513 remote_contact = gajim.contacts.get_first_contact_from_jid(
2514 self.remote_account, remote_jid)
2515 # keep same groups and same nickname
2516 gajim.interface.roster.req_sub(self, remote_jid, message,
2517 self.local_account, groups = remote_contact.groups,
2518 nickname = remote_contact.name, auto_auth = True)
2519 iter_ = model.iter_next(iter_)
2520 self.dialog.destroy()
2522 class NewChatDialog(InputDialog):
2523 def __init__(self, account):
2524 self.account = account
2526 if len(gajim.connections) > 1:
2527 title = _('Start Chat with account %s') % account
2528 else:
2529 title = _('Start Chat')
2530 prompt_text = _('Fill in the nickname or the Jabber ID of the contact you would like\nto send a chat message to:')
2531 InputDialog.__init__(self, title, prompt_text, is_modal=False)
2533 self.completion_dict = {}
2534 liststore = gtkgui_helpers.get_completion_liststore(self.input_entry)
2535 self.completion_dict = helpers.get_contact_dict_for_account(account)
2536 # add all contacts to the model
2537 keys = sorted(self.completion_dict.keys())
2538 for jid in keys:
2539 contact = self.completion_dict[jid]
2540 img = gajim.interface.jabber_state_images['16'][contact.show]
2541 liststore.append((img.get_pixbuf(), jid))
2543 self.ok_handler = self.new_chat_response
2544 okbutton = self.xml.get_object('okbutton')
2545 okbutton.connect('clicked', self.on_okbutton_clicked)
2546 cancelbutton = self.xml.get_object('cancelbutton')
2547 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
2548 self.dialog.show_all()
2550 def new_chat_response(self, jid):
2552 Called when ok button is clicked
2554 if gajim.connections[self.account].connected <= 1:
2555 #if offline or connecting
2556 ErrorDialog(_('Connection not available'),
2557 _('Please make sure you are connected with "%s".') % self.account)
2558 return
2560 if jid in self.completion_dict:
2561 jid = self.completion_dict[jid].jid
2562 else:
2563 try:
2564 jid = helpers.parse_jid(jid)
2565 except helpers.InvalidFormat, e:
2566 ErrorDialog(_('Invalid JID'), e[0])
2567 return
2568 except:
2569 ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".') % jid)
2570 return
2571 gajim.interface.new_chat_from_jid(self.account, jid)
2573 class ChangePasswordDialog:
2574 def __init__(self, account, on_response):
2575 # 'account' can be None if we are about to create our first one
2576 if not account or gajim.connections[account].connected < 2:
2577 ErrorDialog(_('You are not connected to the server'),
2578 _('Without a connection, you can not change your password.'))
2579 raise GajimGeneralException, 'You are not connected to the server'
2580 self.account = account
2581 self.on_response = on_response
2582 self.xml = gtkgui_helpers.get_gtk_builder('change_password_dialog.ui')
2583 self.dialog = self.xml.get_object('change_password_dialog')
2584 self.password1_entry = self.xml.get_object('password1_entry')
2585 self.password2_entry = self.xml.get_object('password2_entry')
2586 self.dialog.connect('response', self.on_dialog_response)
2588 self.dialog.show_all()
2590 def on_dialog_response(self, dialog, response):
2591 if response != gtk.RESPONSE_OK:
2592 dialog.destroy()
2593 self.on_response(None)
2594 return
2595 password1 = self.password1_entry.get_text().decode('utf-8')
2596 if not password1:
2597 ErrorDialog(_('Invalid password'), _('You must enter a password.'))
2598 return
2599 password2 = self.password2_entry.get_text().decode('utf-8')
2600 if password1 != password2:
2601 ErrorDialog(_('Passwords do not match'),
2602 _('The passwords typed in both fields must be identical.'))
2603 return
2604 dialog.destroy()
2605 self.on_response(password1)
2607 class PopupNotificationWindow:
2608 def __init__(self, event_type, jid, account, msg_type='',
2609 path_to_image=None, title=None, text=None):
2610 self.account = account
2611 self.jid = jid
2612 self.msg_type = msg_type
2614 xml = gtkgui_helpers.get_gtk_builder('popup_notification_window.ui')
2615 self.window = xml.get_object('popup_notification_window')
2616 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLTIP)
2617 close_button = xml.get_object('close_button')
2618 event_type_label = xml.get_object('event_type_label')
2619 event_description_label = xml.get_object('event_description_label')
2620 eventbox = xml.get_object('eventbox')
2621 image = xml.get_object('notification_image')
2623 if not text:
2624 text = gajim.get_name_from_jid(account, jid) # default value of text
2625 if not title:
2626 title = ''
2628 event_type_label.set_markup(
2629 '<span foreground="black" weight="bold">%s</span>' %
2630 gobject.markup_escape_text(title))
2632 # set colors [ http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html ]
2633 self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
2635 # default image
2636 if not path_to_image:
2637 path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
2639 if event_type == _('Contact Signed In'):
2640 bg_color = 'limegreen'
2641 elif event_type == _('Contact Signed Out'):
2642 bg_color = 'red'
2643 elif event_type in (_('New Message'), _('New Single Message'),
2644 _('New Private Message'), _('New E-mail')):
2645 bg_color = 'dodgerblue'
2646 elif event_type == _('File Transfer Request'):
2647 bg_color = 'khaki'
2648 elif event_type == _('File Transfer Error'):
2649 bg_color = 'firebrick'
2650 elif event_type in (_('File Transfer Completed'),
2651 _('File Transfer Stopped')):
2652 bg_color = 'yellowgreen'
2653 elif event_type == _('Groupchat Invitation'):
2654 bg_color = 'tan1'
2655 elif event_type == _('Contact Changed Status'):
2656 bg_color = 'thistle2'
2657 else: # Unknown event! Shouldn't happen but deal with it
2658 bg_color = 'white'
2659 popup_bg_color = gtk.gdk.color_parse(bg_color)
2660 close_button.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
2661 eventbox.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
2662 event_description_label.set_markup('<span foreground="black">%s</span>' %
2663 gobject.markup_escape_text(text))
2665 # set the image
2666 image.set_from_file(path_to_image)
2668 # position the window to bottom-right of screen
2669 window_width, self.window_height = self.window.get_size()
2670 gajim.interface.roster.popups_notification_height += self.window_height
2671 pos_x = gajim.config.get('notification_position_x')
2672 if pos_x < 0:
2673 pos_x = gtk.gdk.screen_width() - window_width + pos_x + 1
2674 pos_y = gajim.config.get('notification_position_y')
2675 if pos_y < 0:
2676 pos_y = gtk.gdk.screen_height() - \
2677 gajim.interface.roster.popups_notification_height + pos_y + 1
2678 self.window.move(pos_x, pos_y)
2680 xml.connect_signals(self)
2681 self.window.show_all()
2682 timeout = gajim.config.get('notification_timeout')
2683 gobject.timeout_add_seconds(timeout, self.on_timeout)
2685 def on_close_button_clicked(self, widget):
2686 self.adjust_height_and_move_popup_notification_windows()
2688 def on_timeout(self):
2689 self.adjust_height_and_move_popup_notification_windows()
2691 def adjust_height_and_move_popup_notification_windows(self):
2692 #remove
2693 gajim.interface.roster.popups_notification_height -= self.window_height
2694 self.window.destroy()
2696 if len(gajim.interface.roster.popup_notification_windows) > 0:
2697 # we want to remove the first window added in the list
2698 gajim.interface.roster.popup_notification_windows.pop(0)
2700 # move the rest of popup windows
2701 gajim.interface.roster.popups_notification_height = 0
2702 for window_instance in gajim.interface.roster.popup_notification_windows:
2703 window_width, window_height = window_instance.window.get_size()
2704 gajim.interface.roster.popups_notification_height += window_height
2705 window_instance.window.move(gtk.gdk.screen_width() - window_width,
2706 gtk.gdk.screen_height() - \
2707 gajim.interface.roster.popups_notification_height)
2709 def on_popup_notification_window_button_press_event(self, widget, event):
2710 if event.button != 1:
2711 self.window.destroy()
2712 return
2713 gajim.interface.handle_event(self.account, self.jid, self.msg_type)
2714 self.adjust_height_and_move_popup_notification_windows()
2716 class SingleMessageWindow:
2718 SingleMessageWindow can send or show a received singled message depending on
2719 action argument which can be 'send' or 'receive'
2721 # Keep a reference on windows so garbage collector don't restroy them
2722 instances = []
2723 def __init__(self, account, to='', action='', from_whom='', subject='',
2724 message='', resource='', session=None, form_node=None):
2725 self.instances.append(self)
2726 self.account = account
2727 self.action = action
2729 self.subject = subject
2730 self.message = message
2731 self.to = to
2732 self.from_whom = from_whom
2733 self.resource = resource
2734 self.session = session
2736 self.xml = gtkgui_helpers.get_gtk_builder('single_message_window.ui')
2737 self.window = self.xml.get_object('single_message_window')
2738 self.count_chars_label = self.xml.get_object('count_chars_label')
2739 self.from_label = self.xml.get_object('from_label')
2740 self.from_entry = self.xml.get_object('from_entry')
2741 self.to_label = self.xml.get_object('to_label')
2742 self.to_entry = self.xml.get_object('to_entry')
2743 self.subject_entry = self.xml.get_object('subject_entry')
2744 self.message_scrolledwindow = self.xml.get_object(
2745 'message_scrolledwindow')
2746 self.message_textview = self.xml.get_object('message_textview')
2747 self.message_tv_buffer = self.message_textview.get_buffer()
2748 self.conversation_scrolledwindow = self.xml.get_object(
2749 'conversation_scrolledwindow')
2750 self.conversation_textview = conversation_textview.ConversationTextview(
2751 account)
2752 self.conversation_textview.tv.show()
2753 self.conversation_tv_buffer = self.conversation_textview.tv.get_buffer()
2754 self.xml.get_object('conversation_scrolledwindow').add(
2755 self.conversation_textview.tv)
2757 self.form_widget = None
2758 parent_box = self.xml.get_object('conversation_scrolledwindow').\
2759 get_parent()
2760 if form_node:
2761 dataform = dataforms.ExtendForm(node=form_node)
2762 self.form_widget = dataforms_widget.DataFormWidget(dataform)
2763 self.form_widget.show_all()
2764 parent_box.add(self.form_widget)
2765 parent_box.child_set_property(self.form_widget, 'position',
2766 parent_box.child_get_property(self.xml.get_object(
2767 'conversation_scrolledwindow'), 'position'))
2768 self.action = 'form'
2770 self.send_button = self.xml.get_object('send_button')
2771 self.reply_button = self.xml.get_object('reply_button')
2772 self.send_and_close_button = self.xml.get_object('send_and_close_button')
2773 self.cancel_button = self.xml.get_object('cancel_button')
2774 self.close_button = self.xml.get_object('close_button')
2775 self.message_tv_buffer.connect('changed', self.update_char_counter)
2776 if isinstance(to, list):
2777 jid = ', '.join( [i[0].jid + '/' + i[0].resource for i in to])
2778 self.to_entry.set_text(jid)
2779 self.to_entry.set_sensitive(False)
2780 else:
2781 self.to_entry.set_text(to)
2783 if gajim.config.get('use_speller') and HAS_GTK_SPELL and action == 'send':
2784 try:
2785 lang = gajim.config.get('speller_language')
2786 if not lang:
2787 lang = gajim.LANG
2788 gtkspell.Spell(self.conversation_textview.tv, lang)
2789 gtkspell.Spell(self.message_textview, lang)
2790 except (gobject.GError, TypeError, RuntimeError, OSError):
2791 AspellDictError(lang)
2793 self.prepare_widgets_for(self.action)
2795 # set_text(None) raises TypeError exception
2796 if self.subject is None:
2797 self.subject = ''
2798 self.subject_entry.set_text(self.subject)
2801 if to == '':
2802 liststore = gtkgui_helpers.get_completion_liststore(self.to_entry)
2803 self.completion_dict = helpers.get_contact_dict_for_account(account)
2804 keys = sorted(self.completion_dict.keys())
2805 for jid in keys:
2806 contact = self.completion_dict[jid]
2807 img = gajim.interface.jabber_state_images['16'][contact.show]
2808 liststore.append((img.get_pixbuf(), jid))
2809 else:
2810 self.completion_dict = {}
2811 self.xml.connect_signals(self)
2813 # get window position and size from config
2814 gtkgui_helpers.resize_window(self.window,
2815 gajim.config.get('single-msg-width'),
2816 gajim.config.get('single-msg-height'))
2817 gtkgui_helpers.move_window(self.window,
2818 gajim.config.get('single-msg-x-position'),
2819 gajim.config.get('single-msg-y-position'))
2821 self.window.show_all()
2823 def on_single_message_window_destroy(self, widget):
2824 self.instances.remove(self)
2825 c = gajim.contacts.get_contact_with_highest_priority(self.account,
2826 self.from_whom)
2827 if not c:
2828 # Groupchat is maybe already destroyed
2829 return
2830 if c.is_groupchat() and not self.from_whom in \
2831 gajim.interface.minimized_controls[self.account] and self.action == \
2832 'receive' and gajim.events.get_nb_roster_events(self.account,
2833 self.from_whom, types=['chat', 'normal']) == 0:
2834 gajim.interface.roster.remove_groupchat(self.from_whom, self.account)
2836 def set_cursor_to_end(self):
2837 end_iter = self.message_tv_buffer.get_end_iter()
2838 self.message_tv_buffer.place_cursor(end_iter)
2840 def save_pos(self):
2841 # save the window size and position
2842 x, y = self.window.get_position()
2843 gajim.config.set('single-msg-x-position', x)
2844 gajim.config.set('single-msg-y-position', y)
2845 width, height = self.window.get_size()
2846 gajim.config.set('single-msg-width', width)
2847 gajim.config.set('single-msg-height', height)
2848 gajim.interface.save_config()
2850 def on_single_message_window_delete_event(self, window, ev):
2851 self.save_pos()
2853 def prepare_widgets_for(self, action):
2854 if len(gajim.connections) > 1:
2855 if action == 'send':
2856 title = _('Single Message using account %s') % self.account
2857 else:
2858 title = _('Single Message in account %s') % self.account
2859 else:
2860 title = _('Single Message')
2862 if action == 'send': # prepare UI for Sending
2863 title = _('Send %s') % title
2864 self.send_button.show()
2865 self.send_and_close_button.show()
2866 self.to_label.show()
2867 self.to_entry.show()
2868 self.reply_button.hide()
2869 self.from_label.hide()
2870 self.from_entry.hide()
2871 self.conversation_scrolledwindow.hide()
2872 self.message_scrolledwindow.show()
2874 if self.message: # we come from a reply?
2875 self.message_textview.grab_focus()
2876 self.cancel_button.hide()
2877 self.close_button.show()
2878 self.message_tv_buffer.set_text(self.message)
2879 gobject.idle_add(self.set_cursor_to_end)
2880 else: # we write a new message (not from reply)
2881 self.close_button.hide()
2882 if self.to: # do we already have jid?
2883 self.subject_entry.grab_focus()
2885 elif action == 'receive': # prepare UI for Receiving
2886 title = _('Received %s') % title
2887 self.reply_button.show()
2888 self.from_label.show()
2889 self.from_entry.show()
2890 self.send_button.hide()
2891 self.send_and_close_button.hide()
2892 self.to_label.hide()
2893 self.to_entry.hide()
2894 self.conversation_scrolledwindow.show()
2895 self.message_scrolledwindow.hide()
2897 if self.message:
2898 self.conversation_textview.print_real_text(self.message)
2899 fjid = self.from_whom
2900 if self.resource:
2901 fjid += '/' + self.resource # Full jid of sender (with resource)
2902 self.from_entry.set_text(fjid)
2903 self.from_entry.set_property('editable', False)
2904 self.subject_entry.set_property('editable', False)
2905 self.reply_button.grab_focus()
2906 self.cancel_button.hide()
2907 self.close_button.show()
2908 elif action == 'form': # prepare UI for Receiving
2909 title = _('Form %s') % title
2910 self.send_button.show()
2911 self.send_and_close_button.show()
2912 self.to_label.show()
2913 self.to_entry.show()
2914 self.reply_button.hide()
2915 self.from_label.hide()
2916 self.from_entry.hide()
2917 self.conversation_scrolledwindow.hide()
2918 self.message_scrolledwindow.hide()
2920 self.window.set_title(title)
2922 def on_cancel_button_clicked(self, widget):
2923 self.save_pos()
2924 self.window.destroy()
2926 def on_close_button_clicked(self, widget):
2927 self.save_pos()
2928 self.window.destroy()
2930 def update_char_counter(self, widget):
2931 characters_no = self.message_tv_buffer.get_char_count()
2932 self.count_chars_label.set_text(unicode(characters_no))
2934 def send_single_message(self):
2935 if gajim.connections[self.account].connected <= 1:
2936 # if offline or connecting
2937 ErrorDialog(_('Connection not available'),
2938 _('Please make sure you are connected with "%s".') % self.account)
2939 return
2940 if isinstance(self.to, list):
2941 sender_list = [i[0].jid + '/' + i[0].resource for i in self.to]
2942 else:
2943 sender_list = [self.to_entry.get_text().decode('utf-8')]
2945 for to_whom_jid in sender_list:
2946 if to_whom_jid in self.completion_dict:
2947 to_whom_jid = self.completion_dict[to_whom_jid].jid
2948 try:
2949 to_whom_jid = helpers.parse_jid(to_whom_jid)
2950 except helpers.InvalidFormat:
2951 ErrorDialog(_('Invalid Jabber ID'),
2952 _('It is not possible to send a message to %s, this JID is not '
2953 'valid.') % to_whom_jid)
2954 return
2956 subject = self.subject_entry.get_text().decode('utf-8')
2957 begin, end = self.message_tv_buffer.get_bounds()
2958 message = self.message_tv_buffer.get_text(begin, end).decode('utf-8')
2960 if '/announce/' in to_whom_jid:
2961 gajim.connections[self.account].send_motd(to_whom_jid, subject,
2962 message)
2963 continue
2965 if self.session:
2966 session = self.session
2967 else:
2968 session = gajim.connections[self.account].make_new_session(
2969 to_whom_jid)
2971 if self.form_widget:
2972 form_node = self.form_widget.data_form
2973 else:
2974 form_node = None
2975 # FIXME: allow GPG message some day
2976 gajim.connections[self.account].send_message(to_whom_jid, message,
2977 keyID=None, type_='normal', subject=subject, session=session,
2978 form_node=form_node)
2980 self.subject_entry.set_text('') # we sent ok, clear the subject
2981 self.message_tv_buffer.set_text('') # we sent ok, clear the textview
2983 def on_send_button_clicked(self, widget):
2984 self.send_single_message()
2986 def on_reply_button_clicked(self, widget):
2987 # we create a new blank window to send and we preset RE: and to jid
2988 self.subject = _('RE: %s') % self.subject
2989 self.message = _('%s wrote:\n') % self.from_whom + self.message
2990 # add > at the begining of each line
2991 self.message = self.message.replace('\n', '\n> ') + '\n\n'
2992 self.window.destroy()
2993 SingleMessageWindow(self.account, to=self.from_whom, action='send',
2994 from_whom=self.from_whom, subject=self.subject, message=self.message,
2995 session=self.session)
2997 def on_send_and_close_button_clicked(self, widget):
2998 self.send_single_message()
2999 self.save_pos()
3000 self.window.destroy()
3002 def on_single_message_window_key_press_event(self, widget, event):
3003 if event.keyval == gtk.keysyms.Escape: # ESCAPE
3004 self.save_pos()
3005 self.window.destroy()
3007 class XMLConsoleWindow:
3008 def __init__(self, account):
3009 self.account = account
3011 self.xml = gtkgui_helpers.get_gtk_builder('xml_console_window.ui')
3012 self.window = self.xml.get_object('xml_console_window')
3013 self.input_textview = self.xml.get_object('input_textview')
3014 self.stanzas_log_textview = self.xml.get_object('stanzas_log_textview')
3015 self.input_tv_buffer = self.input_textview.get_buffer()
3016 buffer_ = self.stanzas_log_textview.get_buffer()
3017 end_iter = buffer_.get_end_iter()
3018 buffer_.create_mark('end', end_iter, False)
3020 self.tagIn = buffer_.create_tag('incoming')
3021 color = gajim.config.get('inmsgcolor')
3022 self.tagIn.set_property('foreground', color)
3023 self.tagInPresence = buffer_.create_tag('incoming_presence')
3024 self.tagInPresence.set_property('foreground', color)
3025 self.tagInMessage = buffer_.create_tag('incoming_message')
3026 self.tagInMessage.set_property('foreground', color)
3027 self.tagInIq = buffer_.create_tag('incoming_iq')
3028 self.tagInIq.set_property('foreground', color)
3030 self.tagOut = buffer_.create_tag('outgoing')
3031 color = gajim.config.get('outmsgcolor')
3032 self.tagOut.set_property('foreground', color)
3033 self.tagOutPresence = buffer_.create_tag('outgoing_presence')
3034 self.tagOutPresence.set_property('foreground', color)
3035 self.tagOutMessage = buffer_.create_tag('outgoing_message')
3036 self.tagOutMessage.set_property('foreground', color)
3037 self.tagOutIq = buffer_.create_tag('outgoing_iq')
3038 self.tagOutIq.set_property('foreground', color)
3039 buffer_.create_tag('') # Default tag
3041 self.enabled = True
3042 self.xml.get_object('enable_checkbutton').set_active(True)
3044 self.input_textview.modify_text(
3045 gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
3047 if len(gajim.connections) > 1:
3048 title = _('XML Console for %s') % self.account
3049 else:
3050 title = _('XML Console')
3052 self.window.set_title(title)
3053 self.window.show_all()
3054 gajim.ged.register_event_handler('stanza-received', ged.GUI1,
3055 self._nec_stanza_received)
3056 gajim.ged.register_event_handler('stanza-sent', ged.GUI1,
3057 self._nec_stanza_sent)
3059 self.xml.connect_signals(self)
3061 def on_xml_console_window_destroy(self, widget):
3062 del gajim.interface.instances[self.account]['xml_console']
3063 gajim.ged.remove_event_handler('stanza-received', ged.GUI1,
3064 self._nec_stanza_received)
3065 gajim.ged.remove_event_handler('stanza-sent', ged.GUI1,
3066 self._nec_stanza_sent)
3068 def on_clear_button_clicked(self, widget):
3069 buffer_ = self.stanzas_log_textview.get_buffer()
3070 buffer_.set_text('')
3072 def on_enable_checkbutton_toggled(self, widget):
3073 self.enabled = widget.get_active()
3075 def on_in_stanza_checkbutton_toggled(self, widget):
3076 active = widget.get_active()
3077 self.tagIn.set_property('invisible', active)
3078 self.tagInPresence.set_property('invisible', active)
3079 self.tagInMessage.set_property('invisible', active)
3080 self.tagInIq.set_property('invisible', active)
3082 def on_presence_stanza_checkbutton_toggled(self, widget):
3083 active = widget.get_active()
3084 self.tagInPresence.set_property('invisible', active)
3085 self.tagOutPresence.set_property('invisible', active)
3087 def on_out_stanza_checkbutton_toggled(self, widget):
3088 active = widget.get_active()
3089 self.tagOut.set_property('invisible', active)
3090 self.tagOutPresence.set_property('invisible', active)
3091 self.tagOutMessage.set_property('invisible', active)
3092 self.tagOutIq.set_property('invisible', active)
3094 def on_message_stanza_checkbutton_toggled(self, widget):
3095 active = widget.get_active()
3096 self.tagInMessage.set_property('invisible', active)
3097 self.tagOutMessage.set_property('invisible', active)
3099 def on_iq_stanza_checkbutton_toggled(self, widget):
3100 active = widget.get_active()
3101 self.tagInIq.set_property('invisible', active)
3102 self.tagOutIq.set_property('invisible', active)
3104 def scroll_to_end(self, ):
3105 parent = self.stanzas_log_textview.get_parent()
3106 buffer_ = self.stanzas_log_textview.get_buffer()
3107 end_mark = buffer_.get_mark('end')
3108 if not end_mark:
3109 return False
3110 self.stanzas_log_textview.scroll_to_mark(end_mark, 0, True, 0, 1)
3111 adjustment = parent.get_hadjustment()
3112 adjustment.set_value(0)
3113 return False
3115 def print_stanza(self, stanza, kind):
3116 # kind must be 'incoming' or 'outgoing'
3117 if not self.enabled:
3118 return
3119 if not stanza:
3120 return
3122 buffer = self.stanzas_log_textview.get_buffer()
3123 at_the_end = False
3124 end_iter = buffer.get_end_iter()
3125 end_rect = self.stanzas_log_textview.get_iter_location(end_iter)
3126 visible_rect = self.stanzas_log_textview.get_visible_rect()
3127 if end_rect.y <= (visible_rect.y + visible_rect.height):
3128 at_the_end = True
3129 end_iter = buffer.get_end_iter()
3131 type_ = ''
3132 if stanza[1:9] == 'presence':
3133 type_ = 'presence'
3134 elif stanza[1:8] == 'message':
3135 type_ = 'message'
3136 elif stanza[1:3] == 'iq':
3137 type_ = 'iq'
3139 if type_:
3140 type_ = kind + '_' + type_
3141 else:
3142 type_ = kind # 'incoming' or 'outgoing'
3144 if kind == 'incoming':
3145 buffer.insert_with_tags_by_name(end_iter, '<!-- In -->\n', type_)
3146 elif kind == 'outgoing':
3147 buffer.insert_with_tags_by_name(end_iter, '<!-- Out -->\n', type_)
3148 end_iter = buffer.get_end_iter()
3149 buffer.insert_with_tags_by_name(end_iter, stanza.replace('><', '>\n<') \
3150 + '\n\n', type_)
3151 if at_the_end:
3152 gobject.idle_add(self.scroll_to_end)
3154 def _nec_stanza_received(self, obj):
3155 if obj.conn.name != self.account:
3156 return
3157 self.print_stanza(obj.stanza_str, 'incoming')
3159 def _nec_stanza_sent(self, obj):
3160 if obj.conn.name != self.account:
3161 return
3162 self.print_stanza(obj.stanza_str, 'outgoing')
3164 def on_send_button_clicked(self, widget):
3165 if gajim.connections[self.account].connected <= 1:
3166 # if offline or connecting
3167 ErrorDialog(_('Connection not available'),
3168 _('Please make sure you are connected with "%s".') % \
3169 self.account)
3170 return
3171 begin_iter, end_iter = self.input_tv_buffer.get_bounds()
3172 stanza = self.input_tv_buffer.get_text(begin_iter, end_iter).decode(
3173 'utf-8')
3174 if stanza:
3175 gajim.connections[self.account].send_stanza(stanza)
3176 self.input_tv_buffer.set_text('') # we sent ok, clear the textview
3178 def on_presence_button_clicked(self, widget):
3179 self.input_tv_buffer.set_text(
3180 '<presence><show></show><status></status><priority></priority>'
3181 '</presence>')
3183 def on_iq_button_clicked(self, widget):
3184 self.input_tv_buffer.set_text(
3185 '<iq to="" type=""><query xmlns=""></query></iq>')
3187 def on_message_button_clicked(self, widget):
3188 self.input_tv_buffer.set_text(
3189 '<message to="" type=""><body></body></message>')
3191 def on_expander_activate(self, widget):
3192 if not widget.get_expanded(): # it's the opposite!
3193 # it's expanded!!
3194 self.input_textview.grab_focus()
3196 #Action that can be done with an incoming list of contacts
3197 TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
3198 'remove': _('remove')}
3199 class RosterItemExchangeWindow:
3201 Windows used when someone send you a exchange contact suggestion
3204 def __init__(self, account, action, exchange_list, jid_from,
3205 message_body=None):
3206 self.account = account
3207 self.action = action
3208 self.exchange_list = exchange_list
3209 self.message_body = message_body
3210 self.jid_from = jid_from
3212 show_dialog = False
3214 # Connect to gtk builder
3215 self.xml = gtkgui_helpers.get_gtk_builder(
3216 'roster_item_exchange_window.ui')
3217 self.window = self.xml.get_object('roster_item_exchange_window')
3219 # Add Widgets.
3220 for widget_to_add in ['accept_button_label', 'type_label',
3221 'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
3222 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3224 # Set labels
3225 # self.action can be 'add', 'modify' or 'remove'
3226 self.type_label.set_label(
3227 _('<b>%(jid)s</b> would like you to <b>%(action)s</b> some contacts '
3228 'in your roster.') % {'jid': jid_from,
3229 'action': TRANSLATED_ACTION[self.action]})
3230 if message_body:
3231 buffer_ = self.body_textview.get_buffer()
3232 buffer_.set_text(self.message_body)
3233 else:
3234 self.body_scrolledwindow.hide()
3235 # Treeview
3236 model = gtk.ListStore(bool, str, str, str, str)
3237 self.items_list_treeview.set_model(model)
3238 # columns
3239 renderer1 = gtk.CellRendererToggle()
3240 renderer1.set_property('activatable', True)
3241 renderer1.connect('toggled', self.toggled_callback)
3242 if self.action == 'add':
3243 title = _('Add')
3244 elif self.action == 'modify':
3245 title = _('Modify')
3246 elif self.action == 'delete':
3247 title = _('Delete')
3248 self.items_list_treeview.insert_column_with_attributes(-1, title,
3249 renderer1, active=0)
3250 renderer2 = gtk.CellRendererText()
3251 self.items_list_treeview.insert_column_with_attributes(-1, _('Jabber ID'),
3252 renderer2, text=1)
3253 renderer3 = gtk.CellRendererText()
3254 self.items_list_treeview.insert_column_with_attributes(-1, _('Name'),
3255 renderer3, text=2)
3256 renderer4 = gtk.CellRendererText()
3257 self.items_list_treeview.insert_column_with_attributes(-1, _('Groups'),
3258 renderer4, text=3)
3260 # Init contacts
3261 model = self.items_list_treeview.get_model()
3262 model.clear()
3264 if action == 'add':
3265 for jid in self.exchange_list:
3266 groups = ''
3267 is_in_roster = True
3268 contact = gajim.contacts.get_contact_with_highest_priority(
3269 self.account, jid)
3270 if not contact:
3271 is_in_roster = False
3272 name = self.exchange_list[jid][0]
3273 num_list = len(self.exchange_list[jid][1])
3274 current = 0
3275 for group in self.exchange_list[jid][1]:
3276 current += 1
3277 if contact and not group in contact.groups:
3278 is_in_roster = False
3279 if current == num_list:
3280 groups = groups + group
3281 else:
3282 groups = groups + group + ', '
3283 if not is_in_roster:
3284 show_dialog = True
3285 iter_ = model.append()
3286 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3288 # Change label for accept_button to action name instead of 'OK'.
3289 self.accept_button_label.set_label(_('Add'))
3290 elif action == 'modify':
3291 for jid in self.exchange_list:
3292 groups = ''
3293 is_in_roster = True
3294 is_right = True
3295 contact = gajim.contacts.get_contact_with_highest_priority(
3296 self.account, jid)
3297 name = self.exchange_list[jid][0]
3298 if not contact:
3299 is_in_roster = False
3300 is_right = False
3301 else:
3302 if name != contact.name:
3303 is_right = False
3304 num_list = len(self.exchange_list[jid][1])
3305 current = 0
3306 for group in self.exchange_list[jid][1]:
3307 current += 1
3308 if contact and not group in contact.groups:
3309 is_right = False
3310 if current == num_list:
3311 groups = groups + group
3312 else:
3313 groups = groups + group + ', '
3314 if not is_right and is_in_roster:
3315 show_dialog = True
3316 iter_ = model.append()
3317 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3319 # Change label for accept_button to action name instead of 'OK'.
3320 self.accept_button_label.set_label(_('Modify'))
3321 elif action == 'delete':
3322 for jid in self.exchange_list:
3323 groups = ''
3324 is_in_roster = True
3325 contact = gajim.contacts.get_contact_with_highest_priority(
3326 self.account, jid)
3327 name = self.exchange_list[jid][0]
3328 if not contact:
3329 is_in_roster = False
3330 num_list = len(self.exchange_list[jid][1])
3331 current = 0
3332 for group in self.exchange_list[jid][1]:
3333 current += 1
3334 if current == num_list:
3335 groups = groups + group
3336 else:
3337 groups = groups + group + ', '
3338 if is_in_roster:
3339 show_dialog = True
3340 iter_ = model.append()
3341 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3343 # Change label for accept_button to action name instead of 'OK'.
3344 self.accept_button_label.set_label(_('Delete'))
3346 if show_dialog:
3347 self.window.show_all()
3348 self.xml.connect_signals(self)
3350 def toggled_callback(self, cell, path):
3351 model = self.items_list_treeview.get_model()
3352 iter_ = model.get_iter(path)
3353 model[iter_][0] = not cell.get_active()
3355 def on_accept_button_clicked(self, widget):
3356 model = self.items_list_treeview.get_model()
3357 iter_ = model.get_iter_root()
3358 if self.action == 'add':
3359 a = 0
3360 while iter_:
3361 if model[iter_][0]:
3362 a+=1
3363 # it is selected
3364 #remote_jid = model[iter_][1].decode('utf-8')
3365 message = _('%s suggested me to add you in my roster.'
3366 % self.jid_from)
3367 # keep same groups and same nickname
3368 groups = model[iter_][3].split(', ')
3369 if groups == ['']:
3370 groups = []
3371 jid = model[iter_][1].decode('utf-8')
3372 if gajim.jid_is_transport(self.jid_from):
3373 gajim.connections[self.account].automatically_added.append(
3374 jid)
3375 gajim.interface.roster.req_sub(self, jid, message,
3376 self.account, groups=groups, nickname=model[iter_][2],
3377 auto_auth=True)
3378 iter_ = model.iter_next(iter_)
3379 InformationDialog(_('Added %s contacts') % str(a))
3380 elif self.action == 'modify':
3381 a = 0
3382 while iter_:
3383 if model[iter_][0]:
3384 a+=1
3385 # it is selected
3386 jid = model[iter_][1].decode('utf-8')
3387 # keep same groups and same nickname
3388 groups = model[iter_][3].split(', ')
3389 if groups == ['']:
3390 groups = []
3391 for u in gajim.contacts.get_contact(self.account, jid):
3392 u.name = model[iter_][2]
3393 gajim.connections[self.account].update_contact(jid,
3394 model[iter_][2], groups)
3395 self.draw_contact(jid, account)
3396 # Update opened chat
3397 ctrl = gajim.interface.msg_win_mgr.get_control(jid, self.account)
3398 if ctrl:
3399 ctrl.update_ui()
3400 win = gajim.interface.msg_win_mgr.get_window(jid,
3401 self.account)
3402 win.redraw_tab(ctrl)
3403 win.show_title()
3404 iter_ = model.iter_next(iter_)
3405 elif self.action == 'delete':
3406 a = 0
3407 while iter_:
3408 if model[iter_][0]:
3409 a+=1
3410 # it is selected
3411 jid = model[iter_][1].decode('utf-8')
3412 gajim.connections[self.account].unsubscribe(jid)
3413 gajim.interface.roster.remove_contact(jid, self.account)
3414 gajim.contacts.remove_jid(self.account, jid)
3415 iter_ = model.iter_next(iter_)
3416 InformationDialog(_('Removed %s contacts') % str(a))
3417 self.window.destroy()
3419 def on_cancel_button_clicked(self, widget):
3420 self.window.destroy()
3423 class ItemArchivingPreferencesWindow:
3424 otr_name = ('approve', 'concede', 'forbid', 'oppose', 'prefer', 'require')
3425 otr_index = dict([(j, i) for i, j in enumerate(otr_name)])
3426 save_name = ('body', 'false', 'message', 'stream')
3427 save_index = dict([(j, i) for i, j in enumerate(save_name)])
3429 def __init__(self, account, item):
3430 self.account = account
3431 self.item = item
3432 if self.item and self.item != 'Default':
3433 self.item_config = gajim.connections[self.account].items[self.item]
3434 else:
3435 self.item_config = gajim.connections[self.account].default
3436 self.waiting = None
3438 # Connect to gtk builder
3439 self.xml = gtkgui_helpers.get_gtk_builder(
3440 'item_archiving_preferences_window.ui')
3441 self.window = self.xml.get_object('item_archiving_preferences_window')
3443 # Add Widgets
3444 for widget_to_add in ('jid_entry', 'expire_entry', 'otr_combobox',
3445 'save_combobox', 'cancel_button', 'ok_button', 'progressbar'):
3446 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3448 if self.item:
3449 self.jid_entry.set_text(self.item)
3450 expire_value = self.item_config['expire'] or ''
3451 self.otr_combobox.set_active(self.otr_index[self.item_config['otr']])
3452 self.save_combobox.set_active(
3453 self.save_index[self.item_config['save']])
3454 self.expire_entry.set_text(expire_value)
3456 self.window.set_title(_('Archiving Preferences for %s') % self.account)
3458 self.window.show_all()
3459 self.progressbar.hide()
3460 self.xml.connect_signals(self)
3462 def update_progressbar(self):
3463 if self.waiting:
3464 self.progressbar.pulse()
3465 return True
3466 return False
3468 def on_otr_combobox_changed(self, widget):
3469 otr = self.otr_name[self.otr_combobox.get_active()]
3470 if otr == 'require':
3471 self.save_combobox.set_active(self.save_index['false'])
3473 def on_ok_button_clicked(self, widget):
3474 # Return directly if operation in progress
3475 if self.waiting:
3476 return
3478 item = self.jid_entry.get_text()
3479 otr = self.otr_name[self.otr_combobox.get_active()]
3480 save = self.save_name[self.save_combobox.get_active()]
3481 expire = self.expire_entry.get_text()
3483 if self.item != 'Default':
3484 try:
3485 item = helpers.parse_jid(item)
3486 except helpers.InvalidFormat, s:
3487 pritext = _('Invalid User ID')
3488 ErrorDialog(pritext, str(s))
3489 return
3491 if expire:
3492 try:
3493 if int(expire) < 0 or str(int(expire)) != expire:
3494 raise ValueError
3495 except ValueError:
3496 pritext = _('Invalid expire value')
3497 sectext = _('Expire must be a valid positive integer.')
3498 ErrorDialog(pritext, sectext)
3499 return
3501 if not (item == self.item and expire == self.item_config['expire'] and
3502 otr == self.item_config['otr'] and save == self.item_config['save']):
3503 if not self.item or self.item == item:
3504 if self.item == 'Default':
3505 self.waiting = 'default'
3506 gajim.connections[self.account].set_default(
3507 otr, save, expire)
3508 else:
3509 self.waiting = 'item'
3510 gajim.connections[self.account].append_or_update_item(
3511 item, otr, save, expire)
3512 else:
3513 self.waiting = 'item'
3514 gajim.connections[self.account].append_or_update_item(
3515 item, otr, save, expire)
3516 gajim.connections[self.account].remove_item(self.item)
3517 self.launch_progressbar()
3518 #self.window.destroy()
3520 def on_cancel_button_clicked(self, widget):
3521 self.window.destroy()
3523 def on_item_archiving_preferences_window_destroy(self, widget):
3524 if self.item:
3525 key_name = 'edit_item_archiving_preferences_%s' % self.item
3526 else:
3527 key_name = 'new_item_archiving_preferences'
3528 if key_name in gajim.interface.instances[self.account]:
3529 del gajim.interface.instances[self.account][key_name]
3531 def launch_progressbar(self):
3532 self.progressbar.show()
3533 self.update_progressbar_timeout_id = gobject.timeout_add(
3534 100, self.update_progressbar)
3536 def response_arrived(self, data):
3537 if self.waiting:
3538 self.window.destroy()
3540 def error_arrived(self, error):
3541 if self.waiting:
3542 self.waiting = None
3543 self.progressbar.hide()
3544 pritext = _('There is an error with the form')
3545 sectext = error
3546 ErrorDialog(pritext, sectext)
3549 class ArchivingPreferencesWindow:
3550 auto_name = ('false', 'true')
3551 auto_index = dict([(j, i) for i, j in enumerate(auto_name)])
3552 method_foo_name = ('prefer', 'concede', 'forbid')
3553 method_foo_index = dict([(j, i) for i, j in enumerate(method_foo_name)])
3555 def __init__(self, account):
3556 self.account = account
3557 self.waiting = []
3559 # Connect to glade
3560 self.xml = gtkgui_helpers.get_gtk_builder(
3561 'archiving_preferences_window.ui')
3562 self.window = self.xml.get_object('archiving_preferences_window')
3564 # Add Widgets
3565 for widget_to_add in ('auto_combobox', 'method_auto_combobox',
3566 'method_local_combobox', 'method_manual_combobox', 'close_button',
3567 'item_treeview', 'item_notebook', 'otr_combobox', 'save_combobox',
3568 'expire_entry', 'remove_button', 'edit_button'):
3569 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3571 self.auto_combobox.set_active(
3572 self.auto_index[gajim.connections[self.account].auto])
3573 self.method_auto_combobox.set_active(
3574 self.method_foo_index[gajim.connections[self.account].method_auto])
3575 self.method_local_combobox.set_active(
3576 self.method_foo_index[gajim.connections[self.account].method_local])
3577 self.method_manual_combobox.set_active(
3578 self.method_foo_index[gajim.connections[self.account].\
3579 method_manual])
3581 model = gtk.ListStore(str, str, str, str)
3582 self.item_treeview.set_model(model)
3583 col = gtk.TreeViewColumn('jid')
3584 self.item_treeview.append_column(col)
3585 renderer = gtk.CellRendererText()
3586 col.pack_start(renderer, True)
3587 col.set_attributes(renderer, text=0)
3589 col = gtk.TreeViewColumn('expire')
3590 col.pack_start(renderer, True)
3591 col.set_attributes(renderer, text=1)
3592 self.item_treeview.append_column(col)
3594 col = gtk.TreeViewColumn('otr')
3595 col.pack_start(renderer, True)
3596 col.set_attributes(renderer, text=2)
3597 self.item_treeview.append_column(col)
3599 col = gtk.TreeViewColumn('save')
3600 col.pack_start(renderer, True)
3601 col.set_attributes(renderer, text=3)
3602 self.item_treeview.append_column(col)
3604 self.fill_items()
3606 self.current_item = None
3608 def sort_items(model, iter1, iter2):
3609 item1 = model.get_value(iter1, 0)
3610 item2 = model.get_value(iter2, 0)
3611 if item1 == 'Default':
3612 return -1
3613 if item2 == 'Default':
3614 return 1
3615 if '@' in item1:
3616 if '@' not in item2:
3617 return 1
3618 elif '@' in item2:
3619 return -1
3620 if item1 < item2:
3621 return -1
3622 if item1 > item2:
3623 return 1
3624 # item1 == item2 ? WTF?
3625 return 0
3627 model.set_sort_column_id(0, gtk.SORT_ASCENDING)
3628 model.set_sort_func(0, sort_items)
3630 self.remove_button.set_sensitive(False)
3631 self.edit_button.set_sensitive(False)
3633 self.window.set_title(_('Archiving Preferences for %s') % self.account)
3635 gajim.ged.register_event_handler(
3636 'archiving-preferences-changed-received', ged.GUI1,
3637 self._nec_archiving_changed_received)
3638 gajim.ged.register_event_handler('archiving-error-received', ged.GUI1,
3639 self._nec_archiving_error)
3641 self.window.show_all()
3643 self.xml.connect_signals(self)
3645 def on_add_item_button_clicked(self, widget):
3646 key_name = 'new_item_archiving_preferences'
3647 if key_name in gajim.interface.instances[self.account]:
3648 gajim.interface.instances[self.account][key_name].window.present()
3649 else:
3650 gajim.interface.instances[self.account][key_name] = \
3651 ItemArchivingPreferencesWindow(self.account, '')
3653 def on_remove_item_button_clicked(self, widget):
3654 if not self.current_item:
3655 return
3657 self.waiting.append('itemremove')
3658 sel = self.item_treeview.get_selection()
3659 (model, iter_) = sel.get_selected()
3660 gajim.connections[self.account].remove_item(model[iter_][0])
3661 model.remove(iter_)
3662 self.remove_button.set_sensitive(False)
3663 self.edit_button.set_sensitive(False)
3665 def on_edit_item_button_clicked(self, widget):
3666 if not self.current_item:
3667 return
3669 key_name = 'edit_item_archiving_preferences_%s' % self.current_item
3670 if key_name in gajim.interface.instances[self.account]:
3671 gajim.interface.instances[self.account][key_name].window.present()
3672 else:
3673 gajim.interface.instances[self.account][key_name] = \
3674 ItemArchivingPreferencesWindow(self.account, self.current_item)
3676 def on_item_treeview_cursor_changed(self, widget):
3677 sel = self.item_treeview.get_selection()
3678 (model, iter_) = sel.get_selected()
3679 item = None
3680 if iter_:
3681 item = model[iter_][0]
3682 if self.current_item and self.current_item == item:
3683 return
3685 self.current_item = item
3686 if self.current_item == 'Default':
3687 self.remove_button.set_sensitive(False)
3688 self.edit_button.set_sensitive(True)
3689 elif self.current_item:
3690 self.remove_button.set_sensitive(True)
3691 self.edit_button.set_sensitive(True)
3692 else:
3693 self.remove_button.set_sensitive(False)
3694 self.edit_button.set_sensitive(False)
3696 def on_auto_combobox_changed(self, widget):
3697 save = self.auto_name[widget.get_active()]
3698 gajim.connections[self.account].set_auto(save)
3700 def on_method_foo_combobox_changed(self, widget):
3701 # We retrieve method type from widget name
3702 # ('foo' in 'method_foo_combobox')
3703 method_type = widget.name.split('_')[1]
3704 use = self.method_foo_name[widget.get_active()]
3705 self.waiting.append('method_%s' % method_type)
3706 gajim.connections[self.account].set_method(method_type, use)
3708 def get_child_window(self):
3709 edit_key_name = 'edit_item_archiving_preferences_%s' % self.current_item
3710 new_key_name = 'new_item_archiving_preferences'
3712 if edit_key_name in gajim.interface.instances[self.account]:
3713 return gajim.interface.instances[self.account][edit_key_name]
3715 if new_key_name in gajim.interface.instances[self.account]:
3716 return gajim.interface.instances[self.account][new_key_name]
3718 def _nec_archiving_changed_received(self, obj):
3719 if obj.conn.name != self.account:
3720 return
3721 for key in ('auto', 'method_auto', 'method_local', 'method_manual'):
3722 if key in obj.conf and key in self.waiting:
3723 self.waiting.remove(key)
3724 if 'default' in obj.conf:
3725 key_name = 'edit_item_archiving_preferences_%s' % \
3726 self.current_item
3727 if key_name in gajim.interface.instances[self.account]:
3728 gajim.interface.instances[self.account][key_name].\
3729 response_arrived(obj.conf['default'])
3730 self.fill_items(True)
3731 for jid, pref in obj.new_items.items():
3732 child = self.get_child_window()
3733 if child:
3734 is_new = not child.item
3735 child.response_arrived(pref)
3736 if is_new:
3737 model = self.item_treeview.get_model()
3738 model.append((jid, pref['expire'], pref['otr'],
3739 pref['save']))
3740 continue
3741 self.fill_items(True)
3742 if 'itemremove' in self.waiting and obj.removed_items:
3743 self.waiting.remove('itemremove')
3744 self.fill_items(True)
3746 def fill_items(self, clear=False):
3747 model = self.item_treeview.get_model()
3748 if clear:
3749 model.clear()
3750 default_config = gajim.connections[self.account].default
3751 expire_value = default_config['expire'] or ''
3752 model.append(('Default', expire_value,
3753 default_config['otr'], default_config['save']))
3754 for item, item_config in \
3755 gajim.connections[self.account].items.items():
3756 expire_value = item_config['expire'] or ''
3757 model.append((item, expire_value, item_config['otr'],
3758 item_config['save']))
3760 def _nec_archiving_error(self, obj):
3761 if obj.conn.name != self.account:
3762 return
3763 if self.waiting:
3764 pritext = _('There is an error')
3765 sectext = obj.error_msg
3766 ErrorDialog(pritext, sectext)
3767 self.waiting.pop()
3768 else:
3769 child = self.get_child_window()
3770 if child:
3771 child.error_arrived(obj.error_msg)
3773 def on_close_button_clicked(self, widget):
3774 self.window.destroy()
3776 def on_archiving_preferences_window_destroy(self, widget):
3777 gajim.ged.remove_event_handler(
3778 'archiving-preferences-changed-received', ged.GUI1,
3779 self._nec_archiving_changed_received)
3780 gajim.ged.remove_event_handler('archiving-error-received', ged.GUI1,
3781 self._nec_archiving_error)
3782 if 'archiving_preferences' in gajim.interface.instances[self.account]:
3783 del gajim.interface.instances[self.account]['archiving_preferences']
3786 class PrivacyListWindow:
3788 Window that is used for creating NEW or EDITING already there privacy lists
3791 def __init__(self, account, privacy_list_name, action):
3792 '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
3793 or edit an already existing one'''
3794 self.account = account
3795 self.privacy_list_name = privacy_list_name
3797 # Dicts and Default Values
3798 self.active_rule = ''
3799 self.global_rules = {}
3800 self.list_of_groups = {}
3802 self.max_order = 0
3804 # Default Edit Values
3805 self.edit_rule_type = 'jid'
3806 self.allow_deny = 'allow'
3808 # Connect to gtk builder
3809 self.xml = gtkgui_helpers.get_gtk_builder('privacy_list_window.ui')
3810 self.window = self.xml.get_object('privacy_list_edit_window')
3812 # Add Widgets
3814 for widget_to_add in ('title_hbox', 'privacy_lists_title_label',
3815 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
3816 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
3817 'list_of_rules_combobox', 'delete_open_buttons_hbox',
3818 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
3819 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
3820 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
3821 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
3822 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
3823 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
3824 'edit_view_status_checkbutton', 'edit_all_checkbutton',
3825 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
3826 'privacy_list_refresh_button', 'privacy_list_close_button',
3827 'edit_send_status_checkbutton', 'add_edit_vbox',
3828 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
3829 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3831 self.privacy_lists_title_label.set_label(
3832 _('Privacy List <b><i>%s</i></b>') % \
3833 gobject.markup_escape_text(self.privacy_list_name))
3835 if len(gajim.connections) > 1:
3836 title = _('Privacy List for %s') % self.account
3837 else:
3838 title = _('Privacy List')
3840 self.delete_rule_button.set_sensitive(False)
3841 self.open_rule_button.set_sensitive(False)
3842 self.privacy_list_active_checkbutton.set_sensitive(False)
3843 self.privacy_list_default_checkbutton.set_sensitive(False)
3844 self.list_of_rules_combobox.set_sensitive(False)
3846 # set jabber id completion
3847 jids_list_store = gtk.ListStore(gobject.TYPE_STRING)
3848 for jid in gajim.contacts.get_jid_list(self.account):
3849 jids_list_store.append([jid])
3850 jid_entry_completion = gtk.EntryCompletion()
3851 jid_entry_completion.set_text_column(0)
3852 jid_entry_completion.set_model(jids_list_store)
3853 jid_entry_completion.set_popup_completion(True)
3854 self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
3855 if action == 'EDIT':
3856 self.refresh_rules()
3858 count = 0
3859 for group in gajim.groups[self.account]:
3860 self.list_of_groups[group] = count
3861 count += 1
3862 self.edit_type_group_combobox.append_text(group)
3863 self.edit_type_group_combobox.set_active(0)
3865 self.window.set_title(title)
3867 self.window.show_all()
3868 self.add_edit_vbox.hide()
3870 self.xml.connect_signals(self)
3872 def on_privacy_list_edit_window_destroy(self, widget):
3873 key_name = 'privacy_list_%s' % self.privacy_list_name
3874 if key_name in gajim.interface.instances[self.account]:
3875 del gajim.interface.instances[self.account][key_name]
3877 def check_active_default(self, a_d_dict):
3878 if a_d_dict['active'] == self.privacy_list_name:
3879 self.privacy_list_active_checkbutton.set_active(True)
3880 else:
3881 self.privacy_list_active_checkbutton.set_active(False)
3882 if a_d_dict['default'] == self.privacy_list_name:
3883 self.privacy_list_default_checkbutton.set_active(True)
3884 else:
3885 self.privacy_list_default_checkbutton.set_active(False)
3887 def privacy_list_received(self, rules):
3888 self.list_of_rules_combobox.get_model().clear()
3889 self.global_rules = {}
3890 for rule in rules:
3891 if 'type' in rule:
3892 text_item = _('Order: %(order)s, action: %(action)s, type: %(type)s'
3893 ', value: %(value)s') % {'order': rule['order'],
3894 'action': rule['action'], 'type': rule['type'],
3895 'value': rule['value']}
3896 else:
3897 text_item = _('Order: %(order)s, action: %(action)s') % \
3898 {'order': rule['order'], 'action': rule['action']}
3899 if int(rule['order']) > self.max_order:
3900 self.max_order = int(rule['order'])
3901 self.global_rules[text_item] = rule
3902 self.list_of_rules_combobox.append_text(text_item)
3903 if len(rules) == 0:
3904 self.title_hbox.set_sensitive(False)
3905 self.list_of_rules_combobox.set_sensitive(False)
3906 self.delete_rule_button.set_sensitive(False)
3907 self.open_rule_button.set_sensitive(False)
3908 self.privacy_list_active_checkbutton.set_sensitive(False)
3909 self.privacy_list_default_checkbutton.set_sensitive(False)
3910 else:
3911 self.list_of_rules_combobox.set_active(0)
3912 self.title_hbox.set_sensitive(True)
3913 self.list_of_rules_combobox.set_sensitive(True)
3914 self.delete_rule_button.set_sensitive(True)
3915 self.open_rule_button.set_sensitive(True)
3916 self.privacy_list_active_checkbutton.set_sensitive(True)
3917 self.privacy_list_default_checkbutton.set_sensitive(True)
3918 self.reset_fields()
3919 gajim.connections[self.account].get_active_default_lists()
3921 def refresh_rules(self):
3922 gajim.connections[self.account].get_privacy_list(self.privacy_list_name)
3924 def on_delete_rule_button_clicked(self, widget):
3925 tags = []
3926 for rule in self.global_rules:
3927 if rule != self.list_of_rules_combobox.get_active_text():
3928 tags.append(self.global_rules[rule])
3929 gajim.connections[self.account].set_privacy_list(
3930 self.privacy_list_name, tags)
3931 self.privacy_list_received(tags)
3932 self.add_edit_vbox.hide()
3933 if not tags: # we removed latest rule
3934 if 'privacy_lists' in gajim.interface.instances[self.account]:
3935 win = gajim.interface.instances[self.account]['privacy_lists']
3936 win.remove_privacy_list_from_combobox(self.privacy_list_name)
3937 win.draw_widgets()
3939 def on_open_rule_button_clicked(self, widget):
3940 self.add_edit_rule_label.set_label(
3941 _('<b>Edit a rule</b>'))
3942 active_num = self.list_of_rules_combobox.get_active()
3943 if active_num == -1:
3944 self.active_rule = ''
3945 else:
3946 self.active_rule = \
3947 self.list_of_rules_combobox.get_active_text().decode('utf-8')
3948 if self.active_rule != '':
3949 rule_info = self.global_rules[self.active_rule]
3950 self.edit_order_spinbutton.set_value(int(rule_info['order']))
3951 if 'type' in rule_info:
3952 if rule_info['type'] == 'jid':
3953 self.edit_type_jabberid_radiobutton.set_active(True)
3954 self.edit_type_jabberid_entry.set_text(rule_info['value'])
3955 elif rule_info['type'] == 'group':
3956 self.edit_type_group_radiobutton.set_active(True)
3957 if rule_info['value'] in self.list_of_groups:
3958 self.edit_type_group_combobox.set_active(
3959 self.list_of_groups[rule_info['value']])
3960 else:
3961 self.edit_type_group_combobox.set_active(0)
3962 elif rule_info['type'] == 'subscription':
3963 self.edit_type_subscription_radiobutton.set_active(True)
3964 sub_value = rule_info['value']
3965 if sub_value == 'none':
3966 self.edit_type_subscription_combobox.set_active(0)
3967 elif sub_value == 'both':
3968 self.edit_type_subscription_combobox.set_active(1)
3969 elif sub_value == 'from':
3970 self.edit_type_subscription_combobox.set_active(2)
3971 elif sub_value == 'to':
3972 self.edit_type_subscription_combobox.set_active(3)
3973 else:
3974 self.edit_type_select_all_radiobutton.set_active(True)
3975 else:
3976 self.edit_type_select_all_radiobutton.set_active(True)
3977 self.edit_send_messages_checkbutton.set_active(False)
3978 self.edit_queries_send_checkbutton.set_active(False)
3979 self.edit_view_status_checkbutton.set_active(False)
3980 self.edit_send_status_checkbutton.set_active(False)
3981 self.edit_all_checkbutton.set_active(False)
3982 if not rule_info['child']:
3983 self.edit_all_checkbutton.set_active(True)
3984 else:
3985 if 'presence-out' in rule_info['child']:
3986 self.edit_send_status_checkbutton.set_active(True)
3987 if 'presence-in' in rule_info['child']:
3988 self.edit_view_status_checkbutton.set_active(True)
3989 if 'iq' in rule_info['child']:
3990 self.edit_queries_send_checkbutton.set_active(True)
3991 if 'message' in rule_info['child']:
3992 self.edit_send_messages_checkbutton.set_active(True)
3994 if rule_info['action'] == 'allow':
3995 self.edit_allow_radiobutton.set_active(True)
3996 else:
3997 self.edit_deny_radiobutton.set_active(True)
3998 self.add_edit_vbox.show()
4000 def on_edit_all_checkbutton_toggled(self, widget):
4001 if widget.get_active():
4002 self.edit_send_messages_checkbutton.set_active(True)
4003 self.edit_queries_send_checkbutton.set_active(True)
4004 self.edit_view_status_checkbutton.set_active(True)
4005 self.edit_send_status_checkbutton.set_active(True)
4006 self.edit_send_messages_checkbutton.set_sensitive(False)
4007 self.edit_queries_send_checkbutton.set_sensitive(False)
4008 self.edit_view_status_checkbutton.set_sensitive(False)
4009 self.edit_send_status_checkbutton.set_sensitive(False)
4010 else:
4011 self.edit_send_messages_checkbutton.set_active(False)
4012 self.edit_queries_send_checkbutton.set_active(False)
4013 self.edit_view_status_checkbutton.set_active(False)
4014 self.edit_send_status_checkbutton.set_active(False)
4015 self.edit_send_messages_checkbutton.set_sensitive(True)
4016 self.edit_queries_send_checkbutton.set_sensitive(True)
4017 self.edit_view_status_checkbutton.set_sensitive(True)
4018 self.edit_send_status_checkbutton.set_sensitive(True)
4020 def on_privacy_list_active_checkbutton_toggled(self, widget):
4021 if widget.get_active():
4022 gajim.connections[self.account].set_active_list(
4023 self.privacy_list_name)
4024 else:
4025 gajim.connections[self.account].set_active_list(None)
4027 def on_privacy_list_default_checkbutton_toggled(self, widget):
4028 if widget.get_active():
4029 gajim.connections[self.account].set_default_list(
4030 self.privacy_list_name)
4031 else:
4032 gajim.connections[self.account].set_default_list(None)
4034 def on_new_rule_button_clicked(self, widget):
4035 self.reset_fields()
4036 self.add_edit_vbox.show()
4038 def reset_fields(self):
4039 self.edit_type_jabberid_entry.set_text('')
4040 self.edit_allow_radiobutton.set_active(True)
4041 self.edit_type_jabberid_radiobutton.set_active(True)
4042 self.active_rule = ''
4043 self.edit_send_messages_checkbutton.set_active(False)
4044 self.edit_queries_send_checkbutton.set_active(False)
4045 self.edit_view_status_checkbutton.set_active(False)
4046 self.edit_send_status_checkbutton.set_active(False)
4047 self.edit_all_checkbutton.set_active(False)
4048 self.edit_order_spinbutton.set_value(self.max_order + 1)
4049 self.edit_type_group_combobox.set_active(0)
4050 self.edit_type_subscription_combobox.set_active(0)
4051 self.add_edit_rule_label.set_label(
4052 _('<b>Add a rule</b>'))
4054 def get_current_tags(self):
4055 if self.edit_type_jabberid_radiobutton.get_active():
4056 edit_type = 'jid'
4057 edit_value = self.edit_type_jabberid_entry.get_text()
4058 elif self.edit_type_group_radiobutton.get_active():
4059 edit_type = 'group'
4060 edit_value = self.edit_type_group_combobox.get_active_text()
4061 elif self.edit_type_subscription_radiobutton.get_active():
4062 edit_type = 'subscription'
4063 subs = ['none', 'both', 'from', 'to']
4064 edit_value = subs[self.edit_type_subscription_combobox.get_active()]
4065 elif self.edit_type_select_all_radiobutton.get_active():
4066 edit_type = ''
4067 edit_value = ''
4068 edit_order = str(self.edit_order_spinbutton.get_value_as_int())
4069 if self.edit_allow_radiobutton.get_active():
4070 edit_deny = 'allow'
4071 else:
4072 edit_deny = 'deny'
4073 child = []
4074 if not self.edit_all_checkbutton.get_active():
4075 if self.edit_send_messages_checkbutton.get_active():
4076 child.append('message')
4077 if self.edit_queries_send_checkbutton.get_active():
4078 child.append('iq')
4079 if self.edit_send_status_checkbutton.get_active():
4080 child.append('presence-out')
4081 if self.edit_view_status_checkbutton.get_active():
4082 child.append('presence-in')
4083 if edit_type != '':
4084 return {'order': edit_order, 'action': edit_deny,
4085 'type': edit_type, 'value': edit_value, 'child': child}
4086 return {'order': edit_order, 'action': edit_deny, 'child': child}
4088 def on_save_rule_button_clicked(self, widget):
4089 tags=[]
4090 current_tags = self.get_current_tags()
4091 if int(current_tags['order']) > self.max_order:
4092 self.max_order = int(current_tags['order'])
4093 if self.active_rule == '':
4094 tags.append(current_tags)
4096 for rule in self.global_rules:
4097 if rule != self.active_rule:
4098 tags.append(self.global_rules[rule])
4099 else:
4100 tags.append(current_tags)
4102 gajim.connections[self.account].set_privacy_list(
4103 self.privacy_list_name, tags)
4104 self.refresh_rules()
4105 self.add_edit_vbox.hide()
4106 if 'privacy_lists' in gajim.interface.instances[self.account]:
4107 win = gajim.interface.instances[self.account]['privacy_lists']
4108 win.add_privacy_list_to_combobox(self.privacy_list_name)
4109 win.draw_widgets()
4111 def on_list_of_rules_combobox_changed(self, widget):
4112 self.add_edit_vbox.hide()
4114 def on_edit_type_radiobutton_changed(self, widget, radiobutton):
4115 active_bool = widget.get_active()
4116 if active_bool:
4117 self.edit_rule_type = radiobutton
4119 def on_edit_allow_radiobutton_changed(self, widget, radiobutton):
4120 active_bool = widget.get_active()
4121 if active_bool:
4122 self.allow_deny = radiobutton
4124 def on_close_button_clicked(self, widget):
4125 self.window.destroy()
4127 class PrivacyListsWindow:
4129 Window that is the main window for Privacy Lists; we can list there the
4130 privacy lists and ask to create a new one or edit an already there one
4132 def __init__(self, account):
4133 self.account = account
4134 self.privacy_lists_save = []
4136 self.xml = gtkgui_helpers.get_gtk_builder('privacy_lists_window.ui')
4138 self.window = self.xml.get_object('privacy_lists_first_window')
4139 for widget_to_add in ('list_of_privacy_lists_combobox',
4140 'delete_privacy_list_button', 'open_privacy_list_button',
4141 'new_privacy_list_button', 'new_privacy_list_entry',
4142 'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
4143 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
4145 self.draw_privacy_lists_in_combobox([])
4146 self.privacy_lists_refresh()
4148 self.enabled = True
4150 if len(gajim.connections) > 1:
4151 title = _('Privacy Lists for %s') % self.account
4152 else:
4153 title = _('Privacy Lists')
4155 self.window.set_title(title)
4157 self.window.show_all()
4159 self.xml.connect_signals(self)
4161 def on_privacy_lists_first_window_destroy(self, widget):
4162 if 'privacy_lists' in gajim.interface.instances[self.account]:
4163 del gajim.interface.instances[self.account]['privacy_lists']
4165 def remove_privacy_list_from_combobox(self, privacy_list):
4166 if privacy_list not in self.privacy_lists_save:
4167 return
4168 privacy_list_index = self.privacy_lists_save.index(privacy_list)
4169 self.list_of_privacy_lists_combobox.remove_text(privacy_list_index)
4170 self.privacy_lists_save.remove(privacy_list)
4172 def add_privacy_list_to_combobox(self, privacy_list):
4173 if privacy_list in self.privacy_lists_save:
4174 return
4175 self.list_of_privacy_lists_combobox.append_text(privacy_list)
4176 self.privacy_lists_save.append(privacy_list)
4178 def draw_privacy_lists_in_combobox(self, privacy_lists):
4179 self.list_of_privacy_lists_combobox.set_active(-1)
4180 self.list_of_privacy_lists_combobox.get_model().clear()
4181 self.privacy_lists_save = []
4182 for add_item in privacy_lists:
4183 self.add_privacy_list_to_combobox(add_item)
4184 self.draw_widgets()
4186 def draw_widgets(self):
4187 if len(self.privacy_lists_save) == 0:
4188 self.list_of_privacy_lists_combobox.set_sensitive(False)
4189 self.open_privacy_list_button.set_sensitive(False)
4190 self.delete_privacy_list_button.set_sensitive(False)
4191 else:
4192 self.list_of_privacy_lists_combobox.set_sensitive(True)
4193 self.list_of_privacy_lists_combobox.set_active(0)
4194 self.open_privacy_list_button.set_sensitive(True)
4195 self.delete_privacy_list_button.set_sensitive(True)
4197 def on_close_button_clicked(self, widget):
4198 self.window.destroy()
4200 def on_delete_privacy_list_button_clicked(self, widget):
4201 active_list = self.privacy_lists_save[
4202 self.list_of_privacy_lists_combobox.get_active()]
4203 gajim.connections[self.account].del_privacy_list(active_list)
4205 def privacy_list_removed(self, active_list):
4206 self.privacy_lists_save.remove(active_list)
4207 self.privacy_lists_received({'lists': self.privacy_lists_save})
4209 def privacy_lists_received(self, lists):
4210 if not lists:
4211 return
4212 privacy_lists = []
4213 for privacy_list in lists['lists']:
4214 privacy_lists.append(privacy_list)
4215 self.draw_privacy_lists_in_combobox(privacy_lists)
4217 def privacy_lists_refresh(self):
4218 gajim.connections[self.account].get_privacy_lists()
4220 def on_new_privacy_list_button_clicked(self, widget):
4221 name = self.new_privacy_list_entry.get_text()
4222 if not name:
4223 ErrorDialog(_('Invalid List Name'),
4224 _('You must enter a name to create a privacy list.'))
4225 return
4226 key_name = 'privacy_list_%s' % name
4227 if key_name in gajim.interface.instances[self.account]:
4228 gajim.interface.instances[self.account][key_name].window.present()
4229 else:
4230 gajim.interface.instances[self.account][key_name] = \
4231 PrivacyListWindow(self.account, name, 'NEW')
4232 self.new_privacy_list_entry.set_text('')
4234 def on_privacy_lists_refresh_button_clicked(self, widget):
4235 self.privacy_lists_refresh()
4237 def on_open_privacy_list_button_clicked(self, widget):
4238 name = self.privacy_lists_save[
4239 self.list_of_privacy_lists_combobox.get_active()]
4240 key_name = 'privacy_list_%s' % name
4241 if key_name in gajim.interface.instances[self.account]:
4242 gajim.interface.instances[self.account][key_name].window.present()
4243 else:
4244 gajim.interface.instances[self.account][key_name] = \
4245 PrivacyListWindow(self.account, name, 'EDIT')
4247 class InvitationReceivedDialog:
4248 def __init__(self, account, room_jid, contact_jid, password=None,
4249 comment=None, is_continued=False):
4251 self.room_jid = room_jid
4252 self.account = account
4253 self.password = password
4254 self.is_continued = is_continued
4256 pritext = _('''You are invited to a groupchat''')
4257 #Don't translate $Contact
4258 if is_continued:
4259 sectext = _('$Contact has invited you to join a discussion')
4260 else:
4261 sectext = _('$Contact has invited you to group chat %(room_jid)s')\
4262 % {'room_jid': room_jid}
4263 contact = gajim.contacts.get_first_contact_from_jid(account, contact_jid)
4264 contact_text = contact and contact.name or contact_jid
4265 sectext = sectext.replace('$Contact', contact_text)
4267 if comment: # only if not None and not ''
4268 comment = gobject.markup_escape_text(comment)
4269 comment = _('Comment: %s') % comment
4270 sectext += '\n\n%s' % comment
4271 sectext += '\n\n' + _('Do you want to accept the invitation?')
4273 def on_yes(checked):
4274 try:
4275 if self.is_continued:
4276 gajim.interface.join_gc_room(self.account, self.room_jid,
4277 gajim.nicks[self.account], None, is_continued=True)
4278 else:
4279 JoinGroupchatWindow(self.account, self.room_jid)
4280 except GajimGeneralException:
4281 pass
4283 YesNoDialog(pritext, sectext, on_response_yes=on_yes)
4285 class ProgressDialog:
4286 def __init__(self, title_text, during_text, messages_queue):
4288 During text is what to show during the procedure, messages_queue has the
4289 message to show in the textview
4291 self.xml = gtkgui_helpers.get_gtk_builder('progress_dialog.ui')
4292 self.dialog = self.xml.get_object('progress_dialog')
4293 self.label = self.xml.get_object('label')
4294 self.label.set_markup('<big>' + during_text + '</big>')
4295 self.progressbar = self.xml.get_object('progressbar')
4296 self.dialog.set_title(title_text)
4297 self.dialog.set_default_size(450, 250)
4298 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
4299 self.dialog.show_all()
4300 self.xml.connect_signals(self)
4302 self.update_progressbar_timeout_id = gobject.timeout_add(100,
4303 self.update_progressbar)
4305 def update_progressbar(self):
4306 if self.dialog:
4307 self.progressbar.pulse()
4308 return True # loop forever
4309 return False
4311 def on_progress_dialog_delete_event(self, widget, event):
4312 return True # WM's X button or Escape key should not destroy the window
4315 class ClientCertChooserDialog(FileChooserDialog):
4316 def __init__(self, path_to_clientcert_file='', on_response_ok=None,
4317 on_response_cancel=None):
4319 optionally accepts path_to_clientcert_file so it has that as selected
4321 def on_ok(widget, callback):
4323 check if file exists and call callback
4325 path_to_clientcert_file = self.get_filename()
4326 path_to_clientcert_file = \
4327 gtkgui_helpers.decode_filechooser_file_paths(
4328 (path_to_clientcert_file,))[0]
4329 if os.path.exists(path_to_clientcert_file):
4330 callback(widget, path_to_clientcert_file)
4332 FileChooserDialog.__init__(self,
4333 title_text=_('Choose Client Cert #PCKS12'),
4334 action=gtk.FILE_CHOOSER_ACTION_OPEN,
4335 buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4336 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4337 current_folder='',
4338 default_response=gtk.RESPONSE_OK,
4339 on_response_ok=(on_ok, on_response_ok),
4340 on_response_cancel=on_response_cancel)
4342 filter_ = gtk.FileFilter()
4343 filter_.set_name(_('All files'))
4344 filter_.add_pattern('*')
4345 self.add_filter(filter_)
4347 filter_ = gtk.FileFilter()
4348 filter_.set_name(_('PKCS12 Files'))
4349 filter_.add_pattern('*.p12')
4350 self.add_filter(filter_)
4351 self.set_filter(filter_)
4353 if path_to_clientcert_file:
4354 # set_filename accept only absolute path
4355 path_to_clientcert_file = os.path.abspath(path_to_clientcert_file)
4356 self.set_filename(path_to_clientcert_file)
4359 class SoundChooserDialog(FileChooserDialog):
4360 def __init__(self, path_to_snd_file='', on_response_ok=None,
4361 on_response_cancel=None):
4363 Optionally accepts path_to_snd_file so it has that as selected
4365 def on_ok(widget, callback):
4367 Check if file exists and call callback
4369 path_to_snd_file = self.get_filename()
4370 path_to_snd_file = gtkgui_helpers.decode_filechooser_file_paths(
4371 (path_to_snd_file,))[0]
4372 if os.path.exists(path_to_snd_file):
4373 callback(widget, path_to_snd_file)
4375 FileChooserDialog.__init__(self, title_text = _('Choose Sound'),
4376 action = gtk.FILE_CHOOSER_ACTION_OPEN,
4377 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4378 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4379 default_response = gtk.RESPONSE_OK,
4380 current_folder = gajim.config.get('last_sounds_dir'),
4381 on_response_ok = (on_ok, on_response_ok),
4382 on_response_cancel = on_response_cancel)
4384 filter_ = gtk.FileFilter()
4385 filter_.set_name(_('All files'))
4386 filter_.add_pattern('*')
4387 self.add_filter(filter_)
4389 filter_ = gtk.FileFilter()
4390 filter_.set_name(_('Wav Sounds'))
4391 filter_.add_pattern('*.wav')
4392 self.add_filter(filter_)
4393 self.set_filter(filter_)
4395 path_to_snd_file = helpers.check_soundfile_path(path_to_snd_file)
4396 if path_to_snd_file:
4397 # set_filename accept only absolute path
4398 path_to_snd_file = os.path.abspath(path_to_snd_file)
4399 self.set_filename(path_to_snd_file)
4401 class ImageChooserDialog(FileChooserDialog):
4402 def __init__(self, path_to_file='', on_response_ok=None,
4403 on_response_cancel=None):
4405 Optionally accepts path_to_snd_file so it has that as selected
4407 def on_ok(widget, callback):
4408 '''check if file exists and call callback'''
4409 path_to_file = self.get_filename()
4410 if not path_to_file:
4411 return
4412 path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
4413 (path_to_file,))[0]
4414 if os.path.exists(path_to_file):
4415 if isinstance(callback, tuple):
4416 callback[0](widget, path_to_file, *callback[1:])
4417 else:
4418 callback(widget, path_to_file)
4420 try:
4421 if os.name == 'nt':
4422 path = helpers.get_my_pictures_path()
4423 else:
4424 path = os.environ['HOME']
4425 except Exception:
4426 path = ''
4427 FileChooserDialog.__init__(self,
4428 title_text = _('Choose Image'),
4429 action = gtk.FILE_CHOOSER_ACTION_OPEN,
4430 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4431 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4432 default_response = gtk.RESPONSE_OK,
4433 current_folder = path,
4434 on_response_ok = (on_ok, on_response_ok),
4435 on_response_cancel = on_response_cancel)
4437 if on_response_cancel:
4438 self.connect('destroy', on_response_cancel)
4440 filter_ = gtk.FileFilter()
4441 filter_.set_name(_('All files'))
4442 filter_.add_pattern('*')
4443 self.add_filter(filter_)
4445 filter_ = gtk.FileFilter()
4446 filter_.set_name(_('Images'))
4447 filter_.add_mime_type('image/png')
4448 filter_.add_mime_type('image/jpeg')
4449 filter_.add_mime_type('image/gif')
4450 filter_.add_mime_type('image/tiff')
4451 filter_.add_mime_type('image/svg+xml')
4452 filter_.add_mime_type('image/x-xpixmap') # xpm
4453 self.add_filter(filter_)
4454 self.set_filter(filter_)
4456 if path_to_file:
4457 self.set_filename(path_to_file)
4459 self.set_use_preview_label(False)
4460 self.set_preview_widget(gtk.Image())
4461 self.connect('selection-changed', self.update_preview)
4463 def update_preview(self, widget):
4464 path_to_file = widget.get_preview_filename()
4465 if path_to_file is None or os.path.isdir(path_to_file):
4466 # nothing to preview or directory
4467 # make sure you clean image do show nothing
4468 widget.get_preview_widget().set_from_file(None)
4469 return
4470 try:
4471 pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(path_to_file, 100, 100)
4472 except gobject.GError:
4473 return
4474 widget.get_preview_widget().set_from_pixbuf(pixbuf)
4476 class AvatarChooserDialog(ImageChooserDialog):
4477 def __init__(self, path_to_file='', on_response_ok=None,
4478 on_response_cancel=None, on_response_clear=None):
4479 ImageChooserDialog.__init__(self, path_to_file, on_response_ok,
4480 on_response_cancel)
4481 button = gtk.Button(None, gtk.STOCK_CLEAR)
4482 self.response_clear = on_response_clear
4483 if on_response_clear:
4484 button.connect('clicked', self.on_clear)
4485 button.show_all()
4486 self.action_area.pack_start(button)
4487 self.action_area.reorder_child(button, 0)
4489 def on_clear(self, widget):
4490 if isinstance(self.response_clear, tuple):
4491 self.response_clear[0](widget, *self.response_clear[1:])
4492 else:
4493 self.response_clear(widget)
4496 class ArchiveChooserDialog(FileChooserDialog):
4497 def __init__(self, on_response_ok=None, on_response_cancel=None):
4499 def on_ok(widget, callback):
4500 '''check if file exists and call callback'''
4501 path_to_file = self.get_filename()
4502 if not path_to_file:
4503 return
4504 path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
4505 (path_to_file,))[0]
4506 if os.path.exists(path_to_file):
4507 if isinstance(callback, tuple):
4508 callback[0](path_to_file, *callback[1:])
4509 else:
4510 callback(path_to_file)
4511 self.destroy()
4513 path = helpers.get_documents_path()
4515 FileChooserDialog.__init__(self,
4516 title_text=_('Choose Archive'),
4517 action=gtk.FILE_CHOOSER_ACTION_OPEN,
4518 buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4519 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4520 default_response=gtk.RESPONSE_OK,
4521 current_folder=path,
4522 on_response_ok=(on_ok, on_response_ok),
4523 on_response_cancel=on_response_cancel)
4525 if on_response_cancel:
4526 self.connect('destroy', on_response_cancel)
4528 filter_ = gtk.FileFilter()
4529 filter_.set_name(_('All files'))
4530 filter_.add_pattern('*')
4531 self.add_filter(filter_)
4533 filter_ = gtk.FileFilter()
4534 filter_.set_name(_('Zip files'))
4535 filter_.add_pattern('*.zip')
4537 self.add_filter(filter_)
4538 self.set_filter(filter_)
4541 class AddSpecialNotificationDialog:
4542 def __init__(self, jid):
4544 jid is the jid for which we want to add special notification (sound and
4545 notification popups)
4547 self.xml = gtkgui_helpers.get_gtk_builder(
4548 'add_special_notification_window.ui')
4549 self.window = self.xml.get_object('add_special_notification_window')
4550 self.condition_combobox = self.xml.get_object('condition_combobox')
4551 self.condition_combobox.set_active(0)
4552 self.notification_popup_yes_no_combobox = self.xml.get_object(
4553 'notification_popup_yes_no_combobox')
4554 self.notification_popup_yes_no_combobox.set_active(0)
4555 self.listen_sound_combobox = self.xml.get_object('listen_sound_combobox')
4556 self.listen_sound_combobox.set_active(0)
4558 self.jid = jid
4559 self.xml.get_object('when_foo_becomes_label').set_text(
4560 _('When %s becomes:') % self.jid)
4562 self.window.set_title(_('Adding Special Notification for %s') % jid)
4563 self.window.show_all()
4564 self.xml.connect_signals(self)
4566 def on_cancel_button_clicked(self, widget):
4567 self.window.destroy()
4569 def on_add_special_notification_window_delete_event(self, widget, event):
4570 self.window.destroy()
4572 def on_listen_sound_combobox_changed(self, widget):
4573 active = widget.get_active()
4574 if active == 1: # user selected 'choose sound'
4575 def on_ok(widget, path_to_snd_file):
4576 pass
4577 #print path_to_snd_file
4579 def on_cancel(widget):
4580 widget.set_active(0) # go back to No Sound
4582 self.dialog = SoundChooserDialog(on_response_ok=on_ok,
4583 on_response_cancel=on_cancel)
4585 def on_ok_button_clicked(self, widget):
4586 conditions = ('online', 'chat', 'online_and_chat',
4587 'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline')
4588 active = self.condition_combobox.get_active()
4590 active_iter = self.listen_sound_combobox.get_active_iter()
4591 listen_sound_model = self.listen_sound_combobox.get_model()
4593 class AdvancedNotificationsWindow:
4594 events_list = ['message_received', 'contact_connected',
4595 'contact_disconnected', 'contact_change_status', 'gc_msg_highlight',
4596 'gc_msg', 'ft_request', 'ft_started', 'ft_finished']
4597 recipient_types_list = ['contact', 'group', 'all']
4598 config_options = ['event', 'recipient_type', 'recipients', 'status',
4599 'tab_opened', 'sound', 'sound_file', 'popup', 'auto_open',
4600 'run_command', 'command', 'systray', 'roster', 'urgency_hint']
4601 def __init__(self):
4602 self.xml = gtkgui_helpers.get_gtk_builder(
4603 'advanced_notifications_window.ui')
4604 self.window = self.xml.get_object('advanced_notifications_window')
4605 for w in ('conditions_treeview', 'config_vbox', 'event_combobox',
4606 'recipient_type_combobox', 'recipient_list_entry', 'delete_button',
4607 'status_hbox', 'use_sound_cb', 'disable_sound_cb', 'use_popup_cb',
4608 'disable_popup_cb', 'use_auto_open_cb', 'disable_auto_open_cb',
4609 'use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
4610 'disable_roster_cb', 'tab_opened_cb', 'not_tab_opened_cb',
4611 'sound_entry', 'sound_file_hbox', 'up_button', 'down_button',
4612 'run_command_cb', 'command_entry', 'urgency_hint_cb'):
4613 self.__dict__[w] = self.xml.get_object(w)
4615 # Contains status checkboxes
4616 childs = self.status_hbox.get_children()
4618 self.all_status_rb = childs[0]
4619 self.special_status_rb = childs[1]
4620 self.online_cb = childs[2]
4621 self.away_cb = childs[3]
4622 self.xa_cb = childs[4]
4623 self.dnd_cb = childs[5]
4624 self.invisible_cb = childs[6]
4626 model = gtk.ListStore(int, str)
4627 model.set_sort_column_id(0, gtk.SORT_ASCENDING)
4628 model.clear()
4629 self.conditions_treeview.set_model(model)
4631 ## means number
4632 col = gtk.TreeViewColumn(_('#'))
4633 self.conditions_treeview.append_column(col)
4634 renderer = gtk.CellRendererText()
4635 col.pack_start(renderer, expand=False)
4636 col.set_attributes(renderer, text=0)
4638 col = gtk.TreeViewColumn(_('Condition'))
4639 self.conditions_treeview.append_column(col)
4640 renderer = gtk.CellRendererText()
4641 col.pack_start(renderer, expand=True)
4642 col.set_attributes(renderer, text=1)
4644 self.xml.connect_signals(self)
4646 # Fill conditions_treeview
4647 num = 0
4648 while gajim.config.get_per('notifications', str(num)):
4649 iter_ = model.append((num, ''))
4650 path = model.get_path(iter_)
4651 self.conditions_treeview.set_cursor(path)
4652 self.active_num = num
4653 self.initiate_rule_state()
4654 self.set_treeview_string()
4655 num += 1
4657 # No rule selected at init time
4658 self.conditions_treeview.get_selection().unselect_all()
4659 self.active_num = -1
4660 self.config_vbox.set_sensitive(False)
4661 self.delete_button.set_sensitive(False)
4662 self.down_button.set_sensitive(False)
4663 self.up_button.set_sensitive(False)
4665 self.window.show_all()
4667 def initiate_rule_state(self):
4669 Set values for all widgets
4671 if self.active_num < 0:
4672 return
4673 # event
4674 value = gajim.config.get_per('notifications', str(self.active_num),
4675 'event')
4676 if value:
4677 self.event_combobox.set_active(self.events_list.index(value))
4678 else:
4679 self.event_combobox.set_active(-1)
4680 # recipient_type
4681 value = gajim.config.get_per('notifications', str(self.active_num),
4682 'recipient_type')
4683 if value:
4684 self.recipient_type_combobox.set_active(
4685 self.recipient_types_list.index(value))
4686 else:
4687 self.recipient_type_combobox.set_active(-1)
4688 # recipient
4689 value = gajim.config.get_per('notifications', str(self.active_num),
4690 'recipients')
4691 if not value:
4692 value = ''
4693 self.recipient_list_entry.set_text(value)
4694 # status
4695 value = gajim.config.get_per('notifications', str(self.active_num),
4696 'status')
4697 if value == 'all':
4698 self.all_status_rb.set_active(True)
4699 else:
4700 self.special_status_rb.set_active(True)
4701 values = value.split()
4702 for v in ('online', 'away', 'xa', 'dnd', 'invisible'):
4703 if v in values:
4704 self.__dict__[v + '_cb'].set_active(True)
4705 else:
4706 self.__dict__[v + '_cb'].set_active(False)
4707 self.on_status_radiobutton_toggled(self.all_status_rb)
4708 # tab_opened
4709 value = gajim.config.get_per('notifications', str(self.active_num),
4710 'tab_opened')
4711 self.tab_opened_cb.set_active(True)
4712 self.not_tab_opened_cb.set_active(True)
4713 if value == 'no':
4714 self.tab_opened_cb.set_active(False)
4715 elif value == 'yes':
4716 self.not_tab_opened_cb.set_active(False)
4717 # sound_file
4718 value = gajim.config.get_per('notifications', str(self.active_num),
4719 'sound_file')
4720 self.sound_entry.set_text(value)
4721 # sound, popup, auto_open, systray, roster
4722 for option in ('sound', 'popup', 'auto_open', 'systray', 'roster'):
4723 value = gajim.config.get_per('notifications', str(self.active_num),
4724 option)
4725 if value == 'yes':
4726 self.__dict__['use_' + option + '_cb'].set_active(True)
4727 else:
4728 self.__dict__['use_' + option + '_cb'].set_active(False)
4729 if value == 'no':
4730 self.__dict__['disable_' + option + '_cb'].set_active(True)
4731 else:
4732 self.__dict__['disable_' + option + '_cb'].set_active(False)
4733 # run_command
4734 value = gajim.config.get_per('notifications', str(self.active_num),
4735 'run_command')
4736 self.run_command_cb.set_active(value)
4737 # command
4738 value = gajim.config.get_per('notifications', str(self.active_num),
4739 'command')
4740 self.command_entry.set_text(value)
4741 # urgency_hint
4742 value = gajim.config.get_per('notifications', str(self.active_num),
4743 'urgency_hint')
4744 self.urgency_hint_cb.set_active(value)
4746 def set_treeview_string(self):
4747 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4748 if not iter_:
4749 return
4750 event = self.event_combobox.get_active_text()
4751 recipient_type = self.recipient_type_combobox.get_active_text()
4752 recipient = ''
4753 if recipient_type != 'everybody':
4754 recipient = self.recipient_list_entry.get_text()
4755 if self.all_status_rb.get_active():
4756 status = ''
4757 else:
4758 status = _('when I am ')
4759 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
4760 if self.__dict__[st + '_cb'].get_active():
4761 status += helpers.get_uf_show(st) + ' '
4762 model[iter_][1] = "When %s for %s %s %s" % (event, recipient_type,
4763 recipient, status)
4765 def on_conditions_treeview_cursor_changed(self, widget):
4766 (model, iter_) = widget.get_selection().get_selected()
4767 if not iter_:
4768 self.active_num = -1
4769 return
4770 self.active_num = model[iter_][0]
4771 if self.active_num == 0:
4772 self.up_button.set_sensitive(False)
4773 else:
4774 self.up_button.set_sensitive(True)
4775 max = self.conditions_treeview.get_model().iter_n_children(None)
4776 if self.active_num == max - 1:
4777 self.down_button.set_sensitive(False)
4778 else:
4779 self.down_button.set_sensitive(True)
4780 self.initiate_rule_state()
4781 self.config_vbox.set_sensitive(True)
4782 self.delete_button.set_sensitive(True)
4784 def on_new_button_clicked(self, widget):
4785 model = self.conditions_treeview.get_model()
4786 num = self.conditions_treeview.get_model().iter_n_children(None)
4787 gajim.config.add_per('notifications', str(num))
4788 iter_ = model.append((num, ''))
4789 path = model.get_path(iter_)
4790 self.conditions_treeview.set_cursor(path)
4791 self.active_num = num
4792 self.set_treeview_string()
4793 self.config_vbox.set_sensitive(True)
4795 def on_delete_button_clicked(self, widget):
4796 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4797 if not iter_:
4798 return
4799 # up all others
4800 iter2 = model.iter_next(iter_)
4801 num = self.active_num
4802 while iter2:
4803 num = model[iter2][0]
4804 model[iter2][0] = num - 1
4805 for opt in self.config_options:
4806 val = gajim.config.get_per('notifications', str(num), opt)
4807 gajim.config.set_per('notifications', str(num - 1), opt, val)
4808 iter2 = model.iter_next(iter2)
4809 model.remove(iter_)
4810 gajim.config.del_per('notifications', str(num)) # delete latest
4811 self.active_num = -1
4812 self.config_vbox.set_sensitive(False)
4813 self.delete_button.set_sensitive(False)
4814 self.up_button.set_sensitive(False)
4815 self.down_button.set_sensitive(False)
4817 def on_up_button_clicked(self, widget):
4818 (model, iter_) = self.conditions_treeview.get_selection().\
4819 get_selected()
4820 if not iter_:
4821 return
4822 for opt in self.config_options:
4823 val = gajim.config.get_per('notifications', str(self.active_num),
4824 opt)
4825 val2 = gajim.config.get_per('notifications',
4826 str(self.active_num - 1), opt)
4827 gajim.config.set_per('notifications', str(self.active_num), opt,
4828 val2)
4829 gajim.config.set_per('notifications', str(self.active_num - 1), opt,
4830 val)
4832 model[iter_][0] = self.active_num - 1
4833 # get previous iter
4834 path = model.get_path(iter_)
4835 iter_ = model.get_iter((path[0] - 1,))
4836 model[iter_][0] = self.active_num
4837 self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
4839 def on_down_button_clicked(self, widget):
4840 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4841 if not iter_:
4842 return
4843 for opt in self.config_options:
4844 val = gajim.config.get_per('notifications', str(self.active_num),
4845 opt)
4846 val2 = gajim.config.get_per('notifications',
4847 str(self.active_num + 1), opt)
4848 gajim.config.set_per('notifications', str(self.active_num), opt,
4849 val2)
4850 gajim.config.set_per('notifications', str(self.active_num + 1), opt,
4851 val)
4853 model[iter_][0] = self.active_num + 1
4854 iter_ = model.iter_next(iter_)
4855 model[iter_][0] = self.active_num
4856 self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
4858 def on_event_combobox_changed(self, widget):
4859 if self.active_num < 0:
4860 return
4861 active = self.event_combobox.get_active()
4862 if active == -1:
4863 event = ''
4864 else:
4865 event = self.events_list[active]
4866 gajim.config.set_per('notifications', str(self.active_num), 'event',
4867 event)
4868 self.set_treeview_string()
4870 def on_recipient_type_combobox_changed(self, widget):
4871 if self.active_num < 0:
4872 return
4873 recipient_type = self.recipient_types_list[self.recipient_type_combobox.\
4874 get_active()]
4875 gajim.config.set_per('notifications', str(self.active_num),
4876 'recipient_type', recipient_type)
4877 if recipient_type == 'all':
4878 self.recipient_list_entry.hide()
4879 else:
4880 self.recipient_list_entry.show()
4881 self.set_treeview_string()
4883 def on_recipient_list_entry_changed(self, widget):
4884 if self.active_num < 0:
4885 return
4886 recipients = widget.get_text().decode('utf-8')
4887 #TODO: do some check
4888 gajim.config.set_per('notifications', str(self.active_num),
4889 'recipients', recipients)
4890 self.set_treeview_string()
4892 def set_status_config(self):
4893 if self.active_num < 0:
4894 return
4895 status = ''
4896 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
4897 if self.__dict__[st + '_cb'].get_active():
4898 status += st + ' '
4899 if status:
4900 status = status[:-1]
4901 gajim.config.set_per('notifications', str(self.active_num), 'status',
4902 status)
4903 self.set_treeview_string()
4905 def on_status_radiobutton_toggled(self, widget):
4906 if self.active_num < 0:
4907 return
4908 if self.all_status_rb.get_active():
4909 gajim.config.set_per('notifications', str(self.active_num), 'status',
4910 'all')
4911 # 'All status' clicked
4912 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
4913 self.__dict__[st + '_cb'].hide()
4915 self.special_status_rb.show()
4916 else:
4917 self.set_status_config()
4918 # 'special status' clicked
4919 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
4920 self.__dict__[st + '_cb'].show()
4922 self.special_status_rb.hide()
4923 self.set_treeview_string()
4925 def on_status_cb_toggled(self, widget):
4926 if self.active_num < 0:
4927 return
4928 self.set_status_config()
4930 # tab_opened OR (not xor) not_tab_opened must be active
4931 def on_tab_opened_cb_toggled(self, widget):
4932 if self.active_num < 0:
4933 return
4934 if self.tab_opened_cb.get_active():
4935 if self.not_tab_opened_cb.get_active():
4936 gajim.config.set_per('notifications', str(self.active_num),
4937 'tab_opened', 'both')
4938 else:
4939 gajim.config.set_per('notifications', str(self.active_num),
4940 'tab_opened', 'yes')
4941 elif not self.not_tab_opened_cb.get_active():
4942 self.not_tab_opened_cb.set_active(True)
4943 gajim.config.set_per('notifications', str(self.active_num),
4944 'tab_opened', 'no')
4946 def on_not_tab_opened_cb_toggled(self, widget):
4947 if self.active_num < 0:
4948 return
4949 if self.not_tab_opened_cb.get_active():
4950 if self.tab_opened_cb.get_active():
4951 gajim.config.set_per('notifications', str(self.active_num),
4952 'tab_opened', 'both')
4953 else:
4954 gajim.config.set_per('notifications', str(self.active_num),
4955 'tab_opened', 'no')
4956 elif not self.tab_opened_cb.get_active():
4957 self.tab_opened_cb.set_active(True)
4958 gajim.config.set_per('notifications', str(self.active_num),
4959 'tab_opened', 'yes')
4961 def on_use_it_toggled(self, widget, oposite_widget, option):
4962 if widget.get_active():
4963 if oposite_widget.get_active():
4964 oposite_widget.set_active(False)
4965 gajim.config.set_per('notifications', str(self.active_num), option,
4966 'yes')
4967 elif oposite_widget.get_active():
4968 gajim.config.set_per('notifications', str(self.active_num), option,
4969 'no')
4970 else:
4971 gajim.config.set_per('notifications', str(self.active_num),
4972 option, '')
4974 def on_disable_it_toggled(self, widget, oposite_widget, option):
4975 if widget.get_active():
4976 if oposite_widget.get_active():
4977 oposite_widget.set_active(False)
4978 gajim.config.set_per('notifications', str(self.active_num), option,
4979 'no')
4980 elif oposite_widget.get_active():
4981 gajim.config.set_per('notifications', str(self.active_num), option,
4982 'yes')
4983 else:
4984 gajim.config.set_per('notifications', str(self.active_num), option,
4987 def on_use_sound_cb_toggled(self, widget):
4988 self.on_use_it_toggled(widget, self.disable_sound_cb, 'sound')
4989 if widget.get_active():
4990 self.sound_file_hbox.set_sensitive(True)
4991 else:
4992 self.sound_file_hbox.set_sensitive(False)
4994 def on_browse_for_sounds_button_clicked(self, widget, data=None):
4995 if self.active_num < 0:
4996 return
4998 def on_ok(widget, path_to_snd_file):
4999 dialog.destroy()
5000 if not path_to_snd_file:
5001 path_to_snd_file = ''
5002 gajim.config.set_per('notifications', str(self.active_num),
5003 'sound_file', path_to_snd_file)
5004 self.sound_entry.set_text(path_to_snd_file)
5006 path_to_snd_file = self.sound_entry.get_text().decode('utf-8')
5007 path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file)
5008 dialog = SoundChooserDialog(path_to_snd_file, on_ok)
5010 def on_play_button_clicked(self, widget):
5011 helpers.play_sound_file(self.sound_entry.get_text().decode('utf-8'))
5013 def on_disable_sound_cb_toggled(self, widget):
5014 self.on_disable_it_toggled(widget, self.use_sound_cb, 'sound')
5016 def on_sound_entry_changed(self, widget):
5017 gajim.config.set_per('notifications', str(self.active_num),
5018 'sound_file', widget.get_text().decode('utf-8'))
5020 def on_use_popup_cb_toggled(self, widget):
5021 self.on_use_it_toggled(widget, self.disable_popup_cb, 'popup')
5023 def on_disable_popup_cb_toggled(self, widget):
5024 self.on_disable_it_toggled(widget, self.use_popup_cb, 'popup')
5026 def on_use_auto_open_cb_toggled(self, widget):
5027 self.on_use_it_toggled(widget, self.disable_auto_open_cb, 'auto_open')
5029 def on_disable_auto_open_cb_toggled(self, widget):
5030 self.on_disable_it_toggled(widget, self.use_auto_open_cb, 'auto_open')
5032 def on_run_command_cb_toggled(self, widget):
5033 gajim.config.set_per('notifications', str(self.active_num),
5034 'run_command', widget.get_active())
5035 if widget.get_active():
5036 self.command_entry.set_sensitive(True)
5037 else:
5038 self.command_entry.set_sensitive(False)
5040 def on_command_entry_changed(self, widget):
5041 gajim.config.set_per('notifications', str(self.active_num), 'command',
5042 widget.get_text().decode('utf-8'))
5044 def on_use_systray_cb_toggled(self, widget):
5045 self.on_use_it_toggled(widget, self.disable_systray_cb, 'systray')
5047 def on_disable_systray_cb_toggled(self, widget):
5048 self.on_disable_it_toggled(widget, self.use_systray_cb, 'systray')
5050 def on_use_roster_cb_toggled(self, widget):
5051 self.on_use_it_toggled(widget, self.disable_roster_cb, 'roster')
5053 def on_disable_roster_cb_toggled(self, widget):
5054 self.on_disable_it_toggled(widget, self.use_roster_cb, 'roster')
5056 def on_urgency_hint_cb_toggled(self, widget):
5057 gajim.config.set_per('notifications', str(self.active_num),
5058 'uregency_hint', widget.get_active())
5060 def on_close_window(self, widget):
5061 self.window.destroy()
5063 class TransformChatToMUC:
5064 # Keep a reference on windows so garbage collector don't restroy them
5065 instances = []
5066 def __init__(self, account, jids, preselected=None):
5068 This window is used to trasform a one-to-one chat to a MUC. We do 2
5069 things: first select the server and then make a guests list
5072 self.instances.append(self)
5073 self.account = account
5074 self.auto_jids = jids
5075 self.preselected_jids = preselected
5077 self.xml = gtkgui_helpers.get_gtk_builder('chat_to_muc_window.ui')
5078 self.window = self.xml.get_object('chat_to_muc_window')
5080 for widget_to_add in ('invite_button', 'cancel_button',
5081 'server_list_comboboxentry', 'guests_treeview',
5082 'server_and_guests_hseparator', 'server_select_label'):
5083 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
5085 server_list = []
5086 self.servers = gtk.ListStore(str)
5087 self.server_list_comboboxentry.set_model(self.servers)
5089 self.server_list_comboboxentry.set_text_column(0)
5091 # get the muc server of our server
5092 if 'jabber' in gajim.connections[account].muc_jid:
5093 server_list.append(gajim.connections[account].muc_jid['jabber'])
5094 # add servers or recently joined groupchats
5095 recently_groupchat = gajim.config.get('recently_groupchat').split()
5096 for g in recently_groupchat:
5097 server = gajim.get_server_from_jid(g)
5098 if server not in server_list and not server.startswith('irc'):
5099 server_list.append(server)
5100 # add a default server
5101 if not server_list:
5102 server_list.append('conference.jabber.org')
5104 for s in server_list:
5105 self.servers.append([s])
5107 self.server_list_comboboxentry.set_active(0)
5109 # set treeview
5110 # name, jid
5111 self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
5112 self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
5113 self.guests_treeview.set_model(self.store)
5115 renderer1 = gtk.CellRendererText()
5116 renderer2 = gtk.CellRendererPixbuf()
5117 column = gtk.TreeViewColumn('Status', renderer2, pixbuf=0)
5118 self.guests_treeview.append_column(column)
5119 column = gtk.TreeViewColumn('Name', renderer1, text=1)
5120 self.guests_treeview.append_column(column)
5122 self.guests_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
5124 # All contacts beside the following can be invited:
5125 # transports, zeroconf contacts, minimized groupchats
5126 def invitable(contact, contact_transport=None):
5127 return (contact.jid not in self.auto_jids and
5128 contact.jid != gajim.get_jid_from_account(self.account) and
5129 contact.jid not in gajim.interface.minimized_controls[account] and
5130 not contact.is_transport() and
5131 not contact_transport)
5133 # set jabber id and pseudos
5134 for account in gajim.contacts.get_accounts():
5135 if gajim.connections[account].is_zeroconf:
5136 continue
5137 for jid in gajim.contacts.get_jid_list(account):
5138 contact = gajim.contacts.get_contact_with_highest_priority(
5139 account, jid)
5140 contact_transport = gajim.get_transport_name_from_jid(jid)
5141 # Add contact if it can be invited
5142 if invitable(contact, contact_transport) and \
5143 contact.show not in ('offline', 'error'):
5144 img = gajim.interface.jabber_state_images['16'][contact.show]
5145 name = contact.name
5146 if name == '':
5147 name = jid.split('@')[0]
5148 iter_ = self.store.append([img.get_pixbuf(), name, jid])
5149 # preselect treeview rows
5150 if self.preselected_jids and jid in self.preselected_jids:
5151 path = self.store.get_path(iter_)
5152 self.guests_treeview.get_selection().select_path(path)
5154 # show all
5155 self.window.show_all()
5157 self.xml.connect_signals(self)
5159 def on_chat_to_muc_window_destroy(self, widget):
5160 self.instances.remove(self)
5162 def on_chat_to_muc_window_key_press_event(self, widget, event):
5163 if event.keyval == gtk.keysyms.Escape: # ESCAPE
5164 self.window.destroy()
5166 def on_invite_button_clicked(self, widget):
5167 server = self.server_list_comboboxentry.get_active_text()
5168 if server == '':
5169 return
5170 gajim.connections[self.account].check_unique_room_id_support(server, self)
5172 def unique_room_id_supported(self, server, room_id):
5173 guest_list = []
5174 guests = self.guests_treeview.get_selection().get_selected_rows()
5175 for guest in guests[1]:
5176 iter_ = self.store.get_iter(guest)
5177 guest_list.append(self.store[iter_][2].decode('utf-8'))
5178 for guest in self.auto_jids:
5179 guest_list.append(guest)
5180 room_jid = room_id + '@' + server
5181 gajim.automatic_rooms[self.account][room_jid] = {}
5182 gajim.automatic_rooms[self.account][room_jid]['invities'] = guest_list
5183 gajim.automatic_rooms[self.account][room_jid]['continue_tag'] = True
5184 gajim.interface.join_gc_room(self.account, room_jid,
5185 gajim.nicks[self.account], None, is_continued=True)
5186 self.window.destroy()
5188 def on_cancel_button_clicked(self, widget):
5189 self.window.destroy()
5191 def unique_room_id_error(self, server):
5192 self.unique_room_id_supported(server,
5193 gajim.nicks[self.account].lower().replace(' ', '') + str(randrange(
5194 9999999)))
5196 class DataFormWindow(Dialog):
5197 def __init__(self, form, on_response_ok):
5198 self.df_response_ok = on_response_ok
5199 Dialog.__init__(self, None, 'test', [(gtk.STOCK_CANCEL,
5200 gtk.RESPONSE_REJECT), (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)],
5201 on_response_ok=self.on_ok)
5202 self.set_resizable(True)
5203 gtkgui_helpers.resize_window(self, 600, 400)
5204 self.dataform_widget = dataforms_widget.DataFormWidget()
5205 self.dataform = dataforms.ExtendForm(node=form)
5206 self.dataform_widget.set_sensitive(True)
5207 self.dataform_widget.data_form = self.dataform
5208 self.dataform_widget.show_all()
5209 self.vbox.pack_start(self.dataform_widget)
5211 def on_ok(self):
5212 form = self.dataform_widget.data_form
5213 if isinstance(self.df_response_ok, tuple):
5214 self.df_response_ok[0](form, *self.df_response_ok[1:])
5215 else:
5216 self.df_response_ok(form)
5217 self.destroy()
5219 class ESessionInfoWindow:
5221 Class for displaying information about a XEP-0116 encrypted session
5223 def __init__(self, session):
5224 self.session = session
5226 self.xml = gtkgui_helpers.get_gtk_builder('esession_info_window.ui')
5227 self.xml.connect_signals(self)
5229 self.security_image = self.xml.get_object('security_image')
5230 self.verify_now_button = self.xml.get_object('verify_now_button')
5231 self.button_label = self.xml.get_object('button_label')
5232 self.window = self.xml.get_object('esession_info_window')
5233 self.update_info()
5236 self.window.show_all()
5238 def update_info(self):
5239 labeltext = _('''Your chat session with <b>%(jid)s</b> is encrypted.\n\nThis session's Short Authentication String is <b>%(sas)s</b>.''') % {'jid': self.session.jid, 'sas': self.session.sas}
5241 if self.session.verified_identity:
5242 labeltext += '\n\n' + _('''You have already verified this contact's identity.''')
5243 security_image = 'gajim-security_high'
5244 if self.session.control:
5245 self.session.control._show_lock_image(True, 'E2E', True,
5246 self.session.is_loggable(), True)
5248 verification_status = _('''Contact's identity verified''')
5249 self.window.set_title(verification_status)
5250 self.xml.get_object('verification_status_label').set_markup(
5251 '<b><span size="x-large">%s</span></b>' % verification_status)
5253 self.xml.get_object('dialog-action_area1').set_no_show_all(True)
5254 self.button_label.set_text(_('Verify again...'))
5255 else:
5256 if self.session.control:
5257 self.session.control._show_lock_image(True, 'E2E', True,
5258 self.session.is_loggable(), False)
5259 labeltext += '\n\n' + _('''To be certain that <b>only</b> the expected person can read your messages or send you messages, you need to verify their identity by clicking the button below.''')
5260 security_image = 'gajim-security_low'
5262 verification_status = _('''Contact's identity NOT verified''')
5263 self.window.set_title(verification_status)
5264 self.xml.get_object('verification_status_label').set_markup(
5265 '<b><span size="x-large">%s</span></b>' % verification_status)
5267 self.button_label.set_text(_('Verify...'))
5269 path = gtkgui_helpers.get_icon_path(security_image, 32)
5270 self.security_image.set_from_file(path)
5272 self.xml.get_object('info_display').set_markup(labeltext)
5274 def on_close_button_clicked(self, widget):
5275 self.window.destroy()
5277 def on_verify_now_button_clicked(self, widget):
5278 pritext = _('''Have you verified the contact's identity?''')
5279 sectext = _('''To prevent talking to an unknown person, you should speak to <b>%(jid)s</b> directly (in person or on the phone) and verify that they see the same Short Authentication String (SAS) as you.\n\nThis session's Short Authentication String is <b>%(sas)s</b>.''') % {'jid': self.session.jid, 'sas': self.session.sas}
5280 sectext += '\n\n' + _('Did you talk to the remote contact and verify the SAS?')
5282 def on_yes(checked):
5283 self.session._verified_srs_cb()
5284 self.session.verified_identity = True
5285 self.update_info()
5287 def on_no():
5288 self.session._unverified_srs_cb()
5289 self.session.verified_identity = False
5290 self.update_info()
5292 YesNoDialog(pritext, sectext, on_response_yes=on_yes, on_response_no=on_no)
5294 class GPGInfoWindow:
5296 Class for displaying information about a XEP-0116 encrypted session
5298 def __init__(self, control):
5299 xml = gtkgui_helpers.get_gtk_builder('esession_info_window.ui')
5300 security_image = xml.get_object('security_image')
5301 status_label = xml.get_object('verification_status_label')
5302 info_label = xml.get_object('info_display')
5303 verify_now_button = xml.get_object('verify_now_button')
5304 self.window = xml.get_object('esession_info_window')
5305 account = control.account
5306 keyID = control.contact.keyID
5307 error = None
5309 verify_now_button.set_no_show_all(True)
5310 verify_now_button.hide()
5312 if keyID.endswith('MISMATCH'):
5313 verification_status = _('''Contact's identity NOT verified''')
5314 info = _('The contact\'s key (%s) <b>does not match</b> the key '
5315 'assigned in Gajim.') % keyID[:8]
5316 image = 'gajim-security_low'
5317 elif not keyID:
5318 # No key assigned nor a key is used by remote contact
5319 verification_status = _('No GPG key assigned')
5320 info = _('No GPG key is assigned to this contact. So you cannot '
5321 'encrypt messages.')
5322 image = 'gajim-security_low'
5323 else:
5324 error = gajim.connections[account].gpg.encrypt('test', [keyID])[1]
5325 if error:
5326 verification_status = _('''Contact's identity NOT verified''')
5327 info = _('GPG key is assigned to this contact, but <b>you do not '
5328 'trust his key</b>, so message <b>cannot</b> be encrypted. Use '
5329 'your GPG client to trust this key.')
5330 image = 'gajim-security_low'
5331 else:
5332 verification_status = _('''Contact's identity verified''')
5333 info = _('GPG Key is assigned to this contact, and you trust his '
5334 'key, so messages will be encrypted.')
5335 image = 'gajim-security_high'
5337 status_label.set_markup('<b><span size="x-large">%s</span></b>' % \
5338 verification_status)
5339 info_label.set_markup(info)
5341 path = gtkgui_helpers.get_icon_path(image, 32)
5342 security_image.set_from_file(path)
5344 xml.connect_signals(self)
5345 self.window.show_all()
5347 def on_close_button_clicked(self, widget):
5348 self.window.destroy()
5352 class ResourceConflictDialog(TimeoutDialog, InputDialog):
5353 def __init__(self, title, text, resource, ok_handler):
5354 TimeoutDialog.__init__(self, 15, self.on_timeout)
5355 InputDialog.__init__(self, title, text, input_str=resource,
5356 is_modal=False, ok_handler=ok_handler)
5357 self.title_text = title
5358 self.run_timeout()
5360 def on_timeout(self):
5361 self.on_okbutton_clicked(None)
5365 class VoIPCallReceivedDialog(object):
5366 instances = {}
5367 def __init__(self, account, contact_jid, sid, content_types):
5368 self.instances[(contact_jid, sid)] = self
5369 self.account = account
5370 self.fjid = contact_jid
5371 self.sid = sid
5372 self.content_types = content_types
5374 xml = gtkgui_helpers.get_gtk_builder('voip_call_received_dialog.ui')
5375 xml.connect_signals(self)
5377 jid = gajim.get_jid_without_resource(self.fjid)
5378 contact = gajim.contacts.get_first_contact_from_jid(account, jid)
5379 if contact and contact.name:
5380 self.contact_text = '%s (%s)' % (contact.name, jid)
5381 else:
5382 self.contact_text = contact_jid
5384 self.dialog = xml.get_object('voip_call_received_messagedialog')
5385 self.set_secondary_text()
5387 self.dialog.show_all()
5389 @classmethod
5390 def get_dialog(cls, jid, sid):
5391 if (jid, sid) in cls.instances:
5392 return cls.instances[(jid, sid)]
5393 else:
5394 return None
5396 def set_secondary_text(self):
5397 if 'audio' in self.content_types and 'video' in self.content_types:
5398 types_text = _('an audio and video')
5399 elif 'audio' in self.content_types:
5400 types_text = _('an audio')
5401 elif 'video' in self.content_types:
5402 types_text = _('a video')
5404 # do the substitution
5405 self.dialog.set_property('secondary-text',
5406 _('%(contact)s wants to start %(type)s session with you. Do you want '
5407 'to answer the call?') % {'contact': self.contact_text,
5408 'type': types_text})
5410 def add_contents(self, content_types):
5411 for type_ in content_types:
5412 if type_ not in self.content_types:
5413 self.content_types.add(type_)
5414 self.set_secondary_text()
5416 def remove_contents(self, content_types):
5417 for type_ in content_types:
5418 if type_ in self.content_types:
5419 self.content_types.remove(type_)
5420 if not self.content_types:
5421 self.dialog.destroy()
5422 else:
5423 self.set_secondary_text()
5425 def on_voip_call_received_messagedialog_destroy(self, dialog):
5426 if (self.fjid, self.sid) in self.instances:
5427 del self.instances[(self.fjid, self.sid)]
5429 def on_voip_call_received_messagedialog_close(self, dialog):
5430 return self.on_voip_call_received_messagedialog_response(dialog,
5431 gtk.RESPONSE_NO)
5433 def on_voip_call_received_messagedialog_response(self, dialog, response):
5434 # we've got response from user, either stop connecting or accept the call
5435 session = gajim.connections[self.account].get_jingle_session(self.fjid,
5436 self.sid)
5437 if not session:
5438 return
5439 if response == gtk.RESPONSE_YES:
5440 #TODO: Ensure that ctrl.contact.resource == resource
5441 jid = gajim.get_jid_without_resource(self.fjid)
5442 resource = gajim.get_resource_from_jid(self.fjid)
5443 ctrl = (gajim.interface.msg_win_mgr.get_control(self.fjid, self.account)
5444 or gajim.interface.msg_win_mgr.get_control(jid, self.account)
5445 or gajim.interface.new_chat_from_jid(self.account, jid))
5447 # Chat control opened, update content's status
5448 audio = session.get_content('audio')
5449 video = session.get_content('video')
5450 if audio and not audio.negotiated:
5451 ctrl.set_audio_state('connecting', self.sid)
5452 if video and not video.negotiated:
5453 ctrl.set_video_state('connecting', self.sid)
5454 # Now, accept the content/sessions.
5455 # This should be done after the chat control is running
5456 if not session.accepted:
5457 session.approve_session()
5458 for content in self.content_types:
5459 session.approve_content(content)
5460 else: # response==gtk.RESPONSE_NO
5461 if not session.accepted:
5462 session.decline_session()
5463 else:
5464 for content in self.content_types:
5465 session.reject_content(content)
5467 dialog.destroy()