ability to see certificate information when fingerprint changes. see #3998
[gajim.git] / src / dialogs.py
blob982c55c1790134e8e3beca67a6bebff0e39ed2ee
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
42 from random import randrange
43 from common import pep
44 from common import ged
46 try:
47 import gtkspell
48 HAS_GTK_SPELL = True
49 except ImportError:
50 HAS_GTK_SPELL = False
52 # those imports are not used in this file, but in files that 'import dialogs'
53 # so they can do dialog.GajimThemesWindow() for example
54 from filetransfers_window import FileTransfersWindow
55 from gajim_themes_window import GajimThemesWindow
56 from advanced_configuration_window import AdvancedConfigurationWindow
58 from common import gajim
59 from common import helpers
60 from common import dataforms
61 from common.exceptions import GajimGeneralException
63 class EditGroupsDialog:
64 """
65 Class for the edit group dialog window
66 """
68 def __init__(self, list_):
69 """
70 list_ is a list of (contact, account) tuples
71 """
72 self.xml = gtkgui_helpers.get_gtk_builder('edit_groups_dialog.ui')
73 self.dialog = self.xml.get_object('edit_groups_dialog')
74 self.dialog.set_transient_for(gajim.interface.roster.window)
75 self.list_ = list_
76 self.changes_made = False
77 self.treeview = self.xml.get_object('groups_treeview')
78 if len(list_) == 1:
79 contact = list_[0][0]
80 self.xml.get_object('nickname_label').set_markup(
81 _('Contact name: <i>%s</i>') % contact.get_shown_name())
82 self.xml.get_object('jid_label').set_markup(
83 _('Jabber ID: <i>%s</i>') % contact.jid)
84 else:
85 self.xml.get_object('nickname_label').set_no_show_all(True)
86 self.xml.get_object('nickname_label').hide()
87 self.xml.get_object('jid_label').set_no_show_all(True)
88 self.xml.get_object('jid_label').hide()
90 self.xml.connect_signals(self)
91 self.init_list()
93 self.dialog.show_all()
94 if self.changes_made:
95 for (contact, account) in self.list_:
96 gajim.connections[account].update_contact(contact.jid,
97 contact.name, contact.groups)
99 def on_edit_groups_dialog_response(self, widget, response_id):
100 if response_id == gtk.RESPONSE_CLOSE:
101 self.dialog.destroy()
103 def remove_group(self, group):
105 Remove group group from all contacts and all their brothers
107 for (contact, account) in self.list_:
108 gajim.interface.roster.remove_contact_from_groups(contact.jid,
109 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,
120 [group])
122 # FIXME: Ugly workaround.
123 # Maybe we haven't been in any group (defaults to General)
124 gajim.interface.roster.draw_group(_('General'), account)
126 def on_add_button_clicked(self, widget):
127 group = self.xml.get_object('group_entry').get_text().decode('utf-8')
128 if not group:
129 return
130 # Do not allow special groups
131 if group in helpers.special_groups:
132 return
133 # check if it already exists
134 model = self.treeview.get_model()
135 iter_ = model.get_iter_root()
136 while iter_:
137 if model.get_value(iter_, 0).decode('utf-8') == group:
138 return
139 iter_ = model.iter_next(iter_)
140 self.changes_made = True
141 model.append((group, True, False))
142 self.add_group(group)
143 self.init_list() # Re-draw list to sort new item
145 def group_toggled_cb(self, cell, path):
146 self.changes_made = True
147 model = self.treeview.get_model()
148 if model[path][2]:
149 model[path][2] = False
150 model[path][1] = True
151 else:
152 model[path][1] = not model[path][1]
153 group = model[path][0].decode('utf-8')
154 if model[path][1]:
155 self.add_group(group)
156 else:
157 self.remove_group(group)
159 def init_list(self):
160 store = gtk.ListStore(str, bool, bool)
161 self.treeview.set_model(store)
162 for column in self.treeview.get_columns():
163 # Clear treeview when re-drawing
164 self.treeview.remove_column(column)
165 accounts = []
166 # Store groups in a list so we can sort them and the number of contacts in
167 # it
168 groups = {}
169 for (contact, account) in self.list_:
170 if account not in accounts:
171 accounts.append(account)
172 for g in gajim.groups[account].keys():
173 if g in groups:
174 continue
175 groups[g] = 0
176 c_groups = contact.groups
177 for g in c_groups:
178 groups[g] += 1
179 group_list = []
180 # Remove special groups if they are empty
181 for group in groups:
182 if group not in helpers.special_groups or groups[group] > 0:
183 group_list.append(group)
184 group_list.sort()
185 for group in group_list:
186 iter_ = store.append()
187 store.set(iter_, 0, group) # Group name
188 if groups[group] == 0:
189 store.set(iter_, 1, False)
190 else:
191 store.set(iter_, 1, True)
192 if groups[group] == len(self.list_):
193 # all contacts are in this group
194 store.set(iter_, 2, False)
195 else:
196 store.set(iter_, 2, True)
197 column = gtk.TreeViewColumn(_('Group'))
198 column.set_expand(True)
199 self.treeview.append_column(column)
200 renderer = gtk.CellRendererText()
201 column.pack_start(renderer)
202 column.set_attributes(renderer, text=0)
204 column = gtk.TreeViewColumn(_('In the group'))
205 column.set_expand(False)
206 self.treeview.append_column(column)
207 renderer = gtk.CellRendererToggle()
208 column.pack_start(renderer)
209 renderer.set_property('activatable', True)
210 renderer.connect('toggled', self.group_toggled_cb)
211 column.set_attributes(renderer, active=1, inconsistent=2)
213 class PassphraseDialog:
215 Class for Passphrase dialog
217 def __init__(self, titletext, labeltext, checkbuttontext=None,
218 ok_handler=None, cancel_handler=None):
219 self.xml = gtkgui_helpers.get_gtk_builder('passphrase_dialog.ui')
220 self.window = self.xml.get_object('passphrase_dialog')
221 self.passphrase_entry = self.xml.get_object('passphrase_entry')
222 self.passphrase = -1
223 self.window.set_title(titletext)
224 self.xml.get_object('message_label').set_text(labeltext)
226 self.ok = False
228 self.cancel_handler = cancel_handler
229 self.ok_handler = ok_handler
230 okbutton = self.xml.get_object('ok_button')
231 okbutton.connect('clicked', self.on_okbutton_clicked)
232 cancelbutton = self.xml.get_object('cancel_button')
233 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
235 self.xml.connect_signals(self)
236 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
237 self.window.show_all()
239 self.check = bool(checkbuttontext)
240 checkbutton = self.xml.get_object('save_passphrase_checkbutton')
241 if self.check:
242 checkbutton.set_label(checkbuttontext)
243 else:
244 checkbutton.hide()
246 def on_okbutton_clicked(self, widget):
247 if not self.ok_handler:
248 return
250 passph = self.passphrase_entry.get_text().decode('utf-8')
252 if self.check:
253 checked = self.xml.get_object('save_passphrase_checkbutton').\
254 get_active()
255 else:
256 checked = False
258 self.ok = True
260 self.window.destroy()
262 if isinstance(self.ok_handler, tuple):
263 self.ok_handler[0](passph, checked, *self.ok_handler[1:])
264 else:
265 self.ok_handler(passph, checked)
267 def on_cancelbutton_clicked(self, widget):
268 self.window.destroy()
270 def on_passphrase_dialog_destroy(self, widget):
271 if self.cancel_handler and not self.ok:
272 self.cancel_handler()
274 class ChooseGPGKeyDialog:
276 Class for GPG key dialog
279 def __init__(self, title_text, prompt_text, secret_keys, on_response,
280 selected=None):
281 '''secret_keys : {keyID: userName, ...}'''
282 self.on_response = on_response
283 xml = gtkgui_helpers.get_gtk_builder('choose_gpg_key_dialog.ui')
284 self.window = xml.get_object('choose_gpg_key_dialog')
285 self.window.set_title(title_text)
286 self.keys_treeview = xml.get_object('keys_treeview')
287 prompt_label = xml.get_object('prompt_label')
288 prompt_label.set_text(prompt_text)
289 model = gtk.ListStore(str, str)
290 model.set_sort_func(1, self.sort_keys)
291 model.set_sort_column_id(1, gtk.SORT_ASCENDING)
292 self.keys_treeview.set_model(model)
293 #columns
294 renderer = gtk.CellRendererText()
295 col = self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
296 renderer, text=0)
297 col.set_sort_column_id(0)
298 renderer = gtk.CellRendererText()
299 col = self.keys_treeview.insert_column_with_attributes(-1,
300 _('Contact name'), renderer, text=1)
301 col.set_sort_column_id(1)
302 self.keys_treeview.set_search_column(1)
303 self.fill_tree(secret_keys, selected)
304 self.window.connect('response', self.on_dialog_response)
305 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
306 self.window.show_all()
308 def sort_keys(self, model, iter1, iter2):
309 value1 = model[iter1][1]
310 value2 = model[iter2][1]
311 if value1 == _('None'):
312 return -1
313 elif value2 == _('None'):
314 return 1
315 elif value1 < value2:
316 return -1
317 return 1
319 def on_dialog_response(self, dialog, response):
320 selection = self.keys_treeview.get_selection()
321 (model, iter_) = selection.get_selected()
322 if iter_ and response == gtk.RESPONSE_OK:
323 keyID = [ model[iter_][0].decode('utf-8'),
324 model[iter_][1].decode('utf-8') ]
325 else:
326 keyID = None
327 self.on_response(keyID)
328 self.window.destroy()
330 def fill_tree(self, list_, selected):
331 model = self.keys_treeview.get_model()
332 for keyID in list_.keys():
333 iter_ = model.append((keyID, list_[keyID]))
334 if keyID == selected:
335 path = model.get_path(iter_)
336 self.keys_treeview.set_cursor(path)
339 class ChangeActivityDialog:
340 PAGELIST = ['doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
341 'having_appointment', 'inactive', 'relaxing', 'talking', 'traveling',
342 'working']
344 def __init__(self, on_response, activity=None, subactivity=None, text=''):
345 self.on_response = on_response
346 self.activity = activity
347 self.subactivity = subactivity
348 self.text = text
349 self.xml = gtkgui_helpers.get_gtk_builder('change_activity_dialog.ui')
350 self.window = self.xml.get_object('change_activity_dialog')
351 self.window.set_transient_for(gajim.interface.roster.window)
353 self.checkbutton = self.xml.get_object('enable_checkbutton')
354 self.notebook = self.xml.get_object('notebook')
355 self.entry = self.xml.get_object('description_entry')
357 rbtns = {}
358 group = None
360 for category in pep.ACTIVITIES:
361 item = self.xml.get_object(category + '_image')
362 item.set_from_pixbuf(
363 gtkgui_helpers.load_activity_icon(category).get_pixbuf())
364 item.set_tooltip_text(pep.ACTIVITIES[category]['category'])
366 vbox = self.xml.get_object(category + '_vbox')
367 vbox.set_border_width(5)
369 # Other
370 act = category + '_other'
372 if group:
373 rbtns[act] = gtk.RadioButton(group)
374 else:
375 rbtns[act] = group = gtk.RadioButton()
377 hbox = gtk.HBox(False, 5)
378 hbox.pack_start(gtkgui_helpers.load_activity_icon(category), False,
379 False, 0)
380 lbl = gtk.Label('<b>' + pep.ACTIVITIES[category]['category'] + '</b>')
381 lbl.set_use_markup(True)
382 hbox.pack_start(lbl, False, False, 0)
383 rbtns[act].add(hbox)
384 rbtns[act].connect('toggled', self.on_rbtn_toggled,
385 [category, 'other'])
386 vbox.pack_start(rbtns[act], False, False, 0)
388 activities = []
389 for activity in pep.ACTIVITIES[category]:
390 activities.append(activity)
391 activities.sort()
393 for activity in activities:
394 if activity == 'category':
395 continue
397 act = category + '_' + activity
399 if group:
400 rbtns[act] = gtk.RadioButton(group)
401 else:
402 rbtns[act] = group = gtk.RadioButton()
404 hbox = gtk.HBox(False, 5)
405 hbox.pack_start(gtkgui_helpers.load_activity_icon(category,
406 activity), False, False, 0)
407 hbox.pack_start(gtk.Label(pep.ACTIVITIES[category][activity]),
408 False, False, 0)
409 rbtns[act].connect('toggled', self.on_rbtn_toggled,
410 [category, activity])
411 rbtns[act].add(hbox)
412 vbox.pack_start(rbtns[act], False, False, 0)
415 self.default_radio = rbtns['doing_chores_other']
417 if self.activity in pep.ACTIVITIES:
418 if not self.subactivity in pep.ACTIVITIES[self.activity]:
419 self.subactivity = 'other'
421 rbtns[self.activity + '_' + self.subactivity].set_active(True)
423 self.checkbutton.set_active(True)
424 self.notebook.set_sensitive(True)
425 self.entry.set_sensitive(True)
427 self.notebook.set_current_page(
428 self.PAGELIST.index(self.activity))
430 self.entry.set_text(text)
432 else:
433 self.checkbutton.set_active(False)
435 self.xml.connect_signals(self)
436 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
437 self.window.show_all()
439 def on_enable_checkbutton_toggled(self, widget):
440 self.notebook.set_sensitive(widget.get_active())
441 self.entry.set_sensitive(widget.get_active())
442 if not self.activity:
443 self.default_radio.set_active(True)
445 def on_rbtn_toggled(self, widget, data):
446 if widget.get_active():
447 self.activity = data[0]
448 self.subactivity = data[1]
450 def on_ok_button_clicked(self, widget):
452 Return activity and messsage (None if no activity selected)
454 if self.checkbutton.get_active():
455 self.on_response(self.activity, self.subactivity,
456 self.entry.get_text().decode('utf-8'))
457 else:
458 self.on_response(None, None, '')
459 self.window.destroy()
461 def on_cancel_button_clicked(self, widget):
462 self.window.destroy()
464 class ChangeMoodDialog:
465 COLS = 11
467 def __init__(self, on_response, mood=None, text=''):
468 self.on_response = on_response
469 self.mood = mood
470 self.text = text
471 self.xml = gtkgui_helpers.get_gtk_builder('change_mood_dialog.ui')
473 self.window = self.xml.get_object('change_mood_dialog')
474 self.window.set_transient_for(gajim.interface.roster.window)
475 self.window.set_title(_('Set Mood'))
477 table = self.xml.get_object('mood_icons_table')
478 self.label = self.xml.get_object('mood_label')
479 self.entry = self.xml.get_object('description_entry')
481 no_mood_button = self.xml.get_object('no_mood_button')
482 no_mood_button.set_mode(False)
483 no_mood_button.connect('clicked',
484 self.on_mood_button_clicked, None)
486 x = 1
487 y = 0
488 self.mood_buttons = {}
490 # Order them first
491 self.MOODS = []
492 for mood in pep.MOODS:
493 self.MOODS.append(mood)
494 self.MOODS.sort()
496 for mood in self.MOODS:
497 self.mood_buttons[mood] = gtk.RadioButton(no_mood_button)
498 self.mood_buttons[mood].set_mode(False)
499 self.mood_buttons[mood].add(gtkgui_helpers.load_mood_icon(mood))
500 self.mood_buttons[mood].set_relief(gtk.RELIEF_NONE)
501 self.mood_buttons[mood].set_tooltip_text(pep.MOODS[mood])
502 self.mood_buttons[mood].connect('clicked',
503 self.on_mood_button_clicked, mood)
504 table.attach(self.mood_buttons[mood], x, x + 1, y, y + 1)
506 # Calculate the next position
507 x += 1
508 if x >= self.COLS:
509 x = 0
510 y += 1
512 if self.mood in pep.MOODS:
513 self.mood_buttons[self.mood].set_active(True)
514 self.label.set_text(pep.MOODS[self.mood])
515 self.entry.set_sensitive(True)
516 if self.text:
517 self.entry.set_text(self.text)
518 else:
519 self.label.set_text(_('None'))
520 self.entry.set_text('')
521 self.entry.set_sensitive(False)
523 self.xml.connect_signals(self)
524 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
525 self.window.show_all()
527 def on_mood_button_clicked(self, widget, data):
528 if data:
529 self.label.set_text(pep.MOODS[data])
530 self.entry.set_sensitive(True)
531 else:
532 self.label.set_text(_('None'))
533 self.entry.set_text('')
534 self.entry.set_sensitive(False)
535 self.mood = data
537 def on_ok_button_clicked(self, widget):
538 '''Return mood and messsage (None if no mood selected)'''
539 message = self.entry.get_text().decode('utf-8')
540 self.on_response(self.mood, message)
541 self.window.destroy()
543 def on_cancel_button_clicked(self, widget):
544 self.window.destroy()
546 class TimeoutDialog:
548 Class designed to be derivated to create timeout'd dialogs (dialogs that
549 closes automatically after a timeout)
551 def __init__(self, timeout, on_timeout):
552 self.countdown_left = timeout
553 self.countdown_enabled = True
554 self.title_text = ''
555 self.on_timeout = on_timeout
557 def run_timeout(self):
558 if self.countdown_left > 0:
559 self.countdown()
560 gobject.timeout_add_seconds(1, self.countdown)
562 def on_timeout():
564 To be implemented in derivated classes
566 pass
568 def countdown(self):
569 if self.countdown_enabled:
570 if self.countdown_left <= 0:
571 self.on_timeout()
572 return False
573 self.dialog.set_title('%s [%s]' % (self.title_text,
574 str(self.countdown_left)))
575 self.countdown_left -= 1
576 return True
577 else:
578 self.dialog.set_title(self.title_text)
579 return False
581 class ChangeStatusMessageDialog(TimeoutDialog):
582 def __init__(self, on_response, show=None, show_pep=True):
583 countdown_time = gajim.config.get('change_status_window_timeout')
584 TimeoutDialog.__init__(self, countdown_time, self.on_timeout)
585 self.show = show
586 self.pep_dict = {}
587 self.show_pep = show_pep
588 self.on_response = on_response
589 self.xml = gtkgui_helpers.get_gtk_builder('change_status_message_dialog.ui')
590 self.dialog = self.xml.get_object('change_status_message_dialog')
591 self.dialog.set_transient_for(gajim.interface.roster.window)
592 msg = None
593 if show:
594 uf_show = helpers.get_uf_show(show)
595 self.title_text = _('%s Status Message') % uf_show
596 msg = gajim.config.get_per('statusmsg', '_last_' + self.show,
597 'message')
598 self.pep_dict['activity'] = gajim.config.get_per('statusmsg',
599 '_last_' + self.show, 'activity')
600 self.pep_dict['subactivity'] = gajim.config.get_per('statusmsg',
601 '_last_' + self.show, 'subactivity')
602 self.pep_dict['activity_text'] = gajim.config.get_per('statusmsg',
603 '_last_' + self.show, 'activity_text')
604 self.pep_dict['mood'] = gajim.config.get_per('statusmsg',
605 '_last_' + self.show, 'mood')
606 self.pep_dict['mood_text'] = gajim.config.get_per('statusmsg',
607 '_last_' + self.show, 'mood_text')
608 else:
609 self.title_text = _('Status Message')
610 self.dialog.set_title(self.title_text)
612 message_textview = self.xml.get_object('message_textview')
613 self.message_buffer = message_textview.get_buffer()
614 self.message_buffer.connect('changed', self.on_message_buffer_changed)
615 if not msg:
616 msg = ''
617 msg = helpers.from_one_line(msg)
618 self.message_buffer.set_text(msg)
620 # have an empty string selectable, so user can clear msg
621 self.preset_messages_dict = {'': ['', '', '', '', '', '']}
622 for msg_name in gajim.config.get_per('statusmsg'):
623 if msg_name.startswith('_last_'):
624 continue
625 opts = []
626 for opt in ['message', 'activity', 'subactivity', 'activity_text',
627 'mood', 'mood_text']:
628 opts.append(gajim.config.get_per('statusmsg', msg_name, opt))
629 opts[0] = helpers.from_one_line(opts[0])
630 self.preset_messages_dict[msg_name] = opts
631 sorted_keys_list = helpers.get_sorted_keys(self.preset_messages_dict)
633 self.message_liststore = gtk.ListStore(str) # msg_name
634 self.message_combobox = self.xml.get_object('message_combobox')
635 self.message_combobox.set_model(self.message_liststore)
636 cellrenderertext = gtk.CellRendererText()
637 self.message_combobox.pack_start(cellrenderertext, True)
638 self.message_combobox.add_attribute(cellrenderertext, 'text', 0)
639 for msg_name in sorted_keys_list:
640 self.message_liststore.append((msg_name,))
642 if show_pep:
643 self.draw_activity()
644 self.draw_mood()
645 else:
646 # remove acvtivity / mood lines
647 self.xml.get_object('activity_label').set_no_show_all(True)
648 self.xml.get_object('activity_button').set_no_show_all(True)
649 self.xml.get_object('mood_label').set_no_show_all(True)
650 self.xml.get_object('mood_button').set_no_show_all(True)
651 self.xml.get_object('activity_label').hide()
652 self.xml.get_object('activity_button').hide()
653 self.xml.get_object('mood_label').hide()
654 self.xml.get_object('mood_button').hide()
656 self.xml.connect_signals(self)
657 self.run_timeout()
658 self.dialog.connect('response', self.on_dialog_response)
659 self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
660 self.dialog.show_all()
662 def draw_activity(self):
664 Set activity button
666 img = self.xml.get_object('activity_image')
667 label = self.xml.get_object('activity_button_label')
668 if 'activity' in self.pep_dict and self.pep_dict['activity'] in \
669 pep.ACTIVITIES:
670 if 'subactivity' in self.pep_dict and self.pep_dict['subactivity'] \
671 in pep.ACTIVITIES[self.pep_dict['activity']]:
672 img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
673 self.pep_dict['activity'], self.pep_dict['subactivity']).\
674 get_pixbuf())
675 else:
676 img.set_from_pixbuf(gtkgui_helpers.load_activity_icon(
677 self.pep_dict['activity']).get_pixbuf())
678 if self.pep_dict['activity_text']:
679 label.set_text(self.pep_dict['activity_text'])
680 else:
681 label.set_text('')
682 else:
683 img.set_from_pixbuf(None)
684 label.set_text('')
686 def draw_mood(self):
688 Set mood button
690 img = self.xml.get_object('mood_image')
691 label = self.xml.get_object('mood_button_label')
692 if 'mood' in self.pep_dict and self.pep_dict['mood'] in pep.MOODS:
693 img.set_from_pixbuf(gtkgui_helpers.load_mood_icon(
694 self.pep_dict['mood']).get_pixbuf())
695 if self.pep_dict['mood_text']:
696 label.set_text(self.pep_dict['mood_text'])
697 else:
698 label.set_text('')
699 else:
700 img.set_from_pixbuf(None)
701 label.set_text('')
703 def on_timeout(self):
704 # Prevent GUI freeze when the combobox menu is opened on close
705 self.message_combobox.popdown()
706 self.dialog.response(gtk.RESPONSE_OK)
708 def on_dialog_response(self, dialog, response):
709 if response == gtk.RESPONSE_OK:
710 beg, end = self.message_buffer.get_bounds()
711 message = self.message_buffer.get_text(beg, end).decode('utf-8')\
712 .strip()
713 message = helpers.remove_invalid_xml_chars(message)
714 msg = helpers.to_one_line(message)
715 if self.show:
716 gajim.config.set_per('statusmsg', '_last_' + self.show,
717 'message', msg)
718 if self.show_pep:
719 gajim.config.set_per('statusmsg', '_last_' + self.show,
720 'activity', self.pep_dict['activity'])
721 gajim.config.set_per('statusmsg', '_last_' + self.show,
722 'subactivity', self.pep_dict['subactivity'])
723 gajim.config.set_per('statusmsg', '_last_' + self.show,
724 'activity_text', self.pep_dict['activity_text'])
725 gajim.config.set_per('statusmsg', '_last_' + self.show,
726 'mood', self.pep_dict['mood'])
727 gajim.config.set_per('statusmsg', '_last_' + self.show,
728 'mood_text', self.pep_dict['mood_text'])
729 else:
730 message = None # user pressed Cancel button or X wm button
731 self.dialog.destroy()
732 self.on_response(message, self.pep_dict)
734 def on_message_combobox_changed(self, widget):
735 self.countdown_enabled = False
736 model = widget.get_model()
737 active = widget.get_active()
738 if active < 0:
739 return None
740 name = model[active][0].decode('utf-8')
741 self.message_buffer.set_text(self.preset_messages_dict[name][0])
742 self.pep_dict['activity'] = self.preset_messages_dict[name][1]
743 self.pep_dict['subactivity'] = self.preset_messages_dict[name][2]
744 self.pep_dict['activity_text'] = self.preset_messages_dict[name][3]
745 self.pep_dict['mood'] = self.preset_messages_dict[name][4]
746 self.pep_dict['mood_text'] = self.preset_messages_dict[name][5]
747 self.draw_activity()
748 self.draw_mood()
750 def on_change_status_message_dialog_key_press_event(self, widget, event):
751 self.countdown_enabled = False
752 if event.keyval == gtk.keysyms.Return or \
753 event.keyval == gtk.keysyms.KP_Enter: # catch CTRL+ENTER
754 if (event.state & gtk.gdk.CONTROL_MASK):
755 self.dialog.response(gtk.RESPONSE_OK)
756 # Stop the event
757 return True
759 def on_message_buffer_changed(self, widget):
760 self.countdown_enabled = False
761 self.toggle_sensitiviy_of_save_as_preset()
763 def toggle_sensitiviy_of_save_as_preset(self):
764 btn = self.xml.get_object('save_as_preset_button')
765 if self.message_buffer.get_char_count() == 0:
766 btn.set_sensitive(False)
767 else:
768 btn.set_sensitive(True)
770 def on_save_as_preset_button_clicked(self, widget):
771 self.countdown_enabled = False
772 start_iter, finish_iter = self.message_buffer.get_bounds()
773 status_message_to_save_as_preset = self.message_buffer.get_text(
774 start_iter, finish_iter)
775 def on_ok(msg_name):
776 msg_text = status_message_to_save_as_preset.decode('utf-8')
777 msg_text_1l = helpers.to_one_line(msg_text)
778 if not msg_name: # msg_name was ''
779 msg_name = msg_text_1l.decode('utf-8')
781 def on_ok2():
782 self.preset_messages_dict[msg_name] = [
783 msg_text, self.pep_dict.get('activity'),
784 self.pep_dict.get('subactivity'),
785 self.pep_dict.get('activity_text'),
786 self.pep_dict.get('mood'), self.pep_dict.get('mood_text')]
787 gajim.config.set_per('statusmsg', msg_name, 'message',
788 msg_text_1l)
789 gajim.config.set_per('statusmsg', msg_name, 'activity',
790 self.pep_dict.get('activity'))
791 gajim.config.set_per('statusmsg', msg_name, 'subactivity',
792 self.pep_dict.get('subactivity'))
793 gajim.config.set_per('statusmsg', msg_name, 'activity_text',
794 self.pep_dict.get('activity_text'))
795 gajim.config.set_per('statusmsg', msg_name, 'mood',
796 self.pep_dict.get('mood'))
797 gajim.config.set_per('statusmsg', msg_name, 'mood_text',
798 self.pep_dict.get('mood_text'))
799 if msg_name in self.preset_messages_dict:
800 ConfirmationDialog(_('Overwrite Status Message?'),
801 _('This name is already used. Do you want to overwrite this '
802 'status message?'), on_response_ok=on_ok2)
803 return
804 gajim.config.add_per('statusmsg', msg_name)
805 on_ok2()
806 iter_ = self.message_liststore.append((msg_name,))
807 # select in combobox the one we just saved
808 self.message_combobox.set_active_iter(iter_)
809 InputDialog(_('Save as Preset Status Message'),
810 _('Please type a name for this status message'), is_modal=False,
811 ok_handler=on_ok)
813 def on_activity_button_clicked(self, widget):
814 self.countdown_enabled = False
815 def on_response(activity, subactivity, text):
816 self.pep_dict['activity'] = activity or ''
817 self.pep_dict['subactivity'] = subactivity or ''
818 self.pep_dict['activity_text'] = text
819 self.draw_activity()
820 ChangeActivityDialog(on_response, self.pep_dict['activity'],
821 self.pep_dict['subactivity'], self.pep_dict['activity_text'])
823 def on_mood_button_clicked(self, widget):
824 self.countdown_enabled = False
825 def on_response(mood, text):
826 self.pep_dict['mood'] = mood or ''
827 self.pep_dict['mood_text'] = text
828 self.draw_mood()
829 ChangeMoodDialog(on_response, self.pep_dict['mood'],
830 self.pep_dict['mood_text'])
832 class AddNewContactWindow:
834 Class for AddNewContactWindow
837 uid_labels = {'jabber': _('Jabber ID:'),
838 'aim': _('AIM Address:'),
839 'gadu-gadu': _('GG Number:'),
840 'icq': _('ICQ Number:'),
841 'msn': _('MSN Address:'),
842 'yahoo': _('Yahoo! Address:')}
844 def __init__(self, account=None, jid=None, user_nick=None, group=None):
845 self.account = account
846 self.adding_jid = False
847 if account is None:
848 # fill accounts with active accounts
849 accounts = []
850 for account in gajim.connections.keys():
851 if gajim.connections[account].connected > 1:
852 accounts.append(account)
853 if not accounts:
854 return
855 if len(accounts) == 1:
856 self.account = account
857 else:
858 accounts = [self.account]
859 if self.account:
860 location = gajim.interface.instances[self.account]
861 else:
862 location = gajim.interface.instances
863 if 'add_contact' in location:
864 location['add_contact'].window.present()
865 # An instance is already opened
866 return
867 location['add_contact'] = self
868 self.xml = gtkgui_helpers.get_gtk_builder('add_new_contact_window.ui')
869 self.xml.connect_signals(self)
870 self.window = self.xml.get_object('add_new_contact_window')
871 for w in ('account_combobox', 'account_hbox', 'account_label',
872 'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
873 'protocol_hbox', 'nickname_entry', 'message_scrolledwindow',
874 'save_message_checkbutton', 'register_hbox', 'subscription_table',
875 'add_button', 'message_textview', 'connected_label',
876 'group_comboboxentry', 'auto_authorize_checkbutton'):
877 self.__dict__[w] = self.xml.get_object(w)
878 if account and len(gajim.connections) >= 2:
879 self.default_desc = _('Please fill in the data of the contact you '
880 'want to add in account %s') % account
881 else:
882 self.default_desc = _('Please fill in the data of the contact you '
883 'want to add')
884 self.xml.get_object('prompt_label').set_text(self.default_desc)
885 self.agents = {'jabber': []}
886 self.gateway_prompt = {}
887 # types to which we are not subscribed but account has an agent for it
888 self.available_types = []
889 for acct in accounts:
890 for j in gajim.contacts.get_jid_list(acct):
891 if gajim.jid_is_transport(j):
892 type_ = gajim.get_transport_name_from_jid(j, False)
893 if not type_:
894 continue
895 if type_ in self.agents:
896 self.agents[type_].append(j)
897 else:
898 self.agents[type_] = [j]
899 self.gateway_prompt[j] = {'desc': None, 'prompt': None}
900 # Now add the one to which we can register
901 for acct in accounts:
902 for type_ in gajim.connections[acct].available_transports:
903 if type_ in self.agents:
904 continue
905 self.agents[type_] = []
906 for jid_ in gajim.connections[acct].available_transports[type_]:
907 if not jid_ in self.agents[type_]:
908 self.agents[type_].append(jid_)
909 self.gateway_prompt[jid_] = {'desc': None,
910 'prompt': None}
911 self.available_types.append(type_)
912 # Combobox with transport/jabber icons
913 liststore = gtk.ListStore(str, gtk.gdk.Pixbuf, str)
914 cell = gtk.CellRendererPixbuf()
915 self.protocol_combobox.pack_start(cell, False)
916 self.protocol_combobox.add_attribute(cell, 'pixbuf', 1)
917 cell = gtk.CellRendererText()
918 cell.set_property('xpad', 5)
919 self.protocol_combobox.pack_start(cell, True)
920 self.protocol_combobox.add_attribute(cell, 'text', 0)
921 self.protocol_combobox.set_model(liststore)
922 uf_type = {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
923 'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
924 # Jabber as first
925 img = gajim.interface.jabber_state_images['16']['online']
926 liststore.append(['Jabber', img.get_pixbuf(), 'jabber'])
927 for type_ in self.agents:
928 if type_ == 'jabber':
929 continue
930 imgs = gajim.interface.roster.transports_state_images
931 img = None
932 if type_ in imgs['16'] and 'online' in imgs['16'][type_]:
933 img = imgs['16'][type_]['online']
934 if type_ in uf_type:
935 liststore.append([uf_type[type_], img.get_pixbuf(), type_])
936 else:
937 liststore.append([type_, img.get_pixbuf(), type_])
938 else:
939 liststore.append([type_, img, type_])
940 if account:
941 for service in self.agents[type_]:
942 gajim.connections[account].request_gateway_prompt(service)
943 self.protocol_combobox.set_active(0)
944 self.auto_authorize_checkbutton.show()
945 liststore = gtk.ListStore(str)
946 self.protocol_jid_combobox.set_model(liststore)
947 if jid:
948 type_ = gajim.get_transport_name_from_jid(jid)
949 if not type_:
950 type_ = 'jabber'
951 if type_ == 'jabber':
952 self.uid_entry.set_text(jid)
953 else:
954 uid, transport = gajim.get_name_and_server_from_jid(jid)
955 self.uid_entry.set_text(uid.replace('%', '@', 1))
956 # set protocol_combobox
957 model = self.protocol_combobox.get_model()
958 iter_ = model.get_iter_first()
959 i = 0
960 while iter_:
961 if model[iter_][2] == type_:
962 self.protocol_combobox.set_active(i)
963 break
964 iter_ = model.iter_next(iter_)
965 i += 1
967 # set protocol_jid_combobox
968 self.protocol_jid_combobox.set_active(0)
969 model = self.protocol_jid_combobox.get_model()
970 iter_ = model.get_iter_first()
971 i = 0
972 while iter_:
973 if model[iter_][0] == transport:
974 self.protocol_jid_combobox.set_active(i)
975 break
976 iter_ = model.iter_next(iter_)
977 i += 1
978 if user_nick:
979 self.nickname_entry.set_text(user_nick)
980 self.nickname_entry.grab_focus()
981 else:
982 self.uid_entry.grab_focus()
983 group_names = []
984 for acct in accounts:
985 for g in gajim.groups[acct].keys():
986 if g not in helpers.special_groups and g not in group_names:
987 group_names.append(g)
988 group_names.sort()
989 i = 0
990 for g in group_names:
991 self.group_comboboxentry.append_text(g)
992 if group == g:
993 self.group_comboboxentry.set_active(i)
994 i += 1
996 self.window.set_transient_for(gajim.interface.roster.window)
997 self.window.show_all()
999 if self.account:
1000 self.account_label.hide()
1001 self.account_hbox.hide()
1002 else:
1003 liststore = gtk.ListStore(str, str)
1004 for acct in accounts:
1005 liststore.append([acct, acct])
1006 self.account_combobox.set_model(liststore)
1007 self.account_combobox.set_active(0)
1009 if self.account:
1010 message_buffer = self.message_textview.get_buffer()
1011 message_buffer.set_text(helpers.get_subscription_request_msg(
1012 self.account))
1014 gajim.ged.register_event_handler('gateway-prompt-received', ged.GUI1,
1015 self._nec_gateway_prompt_received)
1016 gajim.ged.register_event_handler('presence-received', ged.GUI1,
1017 self._nec_presence_received)
1019 def on_add_new_contact_window_destroy(self, widget):
1020 if self.account:
1021 location = gajim.interface.instances[self.account]
1022 else:
1023 location = gajim.interface.instances
1024 del location['add_contact']
1025 gajim.ged.remove_event_handler('presence-received', ged.GUI1,
1026 self._nec_presence_received)
1027 gajim.ged.remove_event_handler('gateway-prompt-received', ged.GUI1,
1028 self._nec_gateway_prompt_received)
1030 def on_register_button_clicked(self, widget):
1031 jid = self.protocol_jid_combobox.get_active_text().decode('utf-8')
1032 gajim.connections[self.account].request_register_agent_info(jid)
1034 def on_add_new_contact_window_key_press_event(self, widget, event):
1035 if event.keyval == gtk.keysyms.Escape: # ESCAPE
1036 self.window.destroy()
1038 def on_cancel_button_clicked(self, widget):
1040 When Cancel button is clicked
1042 self.window.destroy()
1044 def on_add_button_clicked(self, widget):
1046 When Subscribe button is clicked
1048 jid = self.uid_entry.get_text().decode('utf-8').strip()
1049 if not jid:
1050 return
1052 model = self.protocol_combobox.get_model()
1053 iter_ = self.protocol_combobox.get_active_iter()
1054 type_ = model[iter_][2]
1055 if type_ != 'jabber':
1056 transport = self.protocol_jid_combobox.get_active_text().decode(
1057 'utf-8')
1058 if self.account:
1059 self.adding_jid = (jid, transport, type_)
1060 gajim.connections[self.account].request_gateway_prompt(
1061 transport, jid)
1062 else:
1063 jid = jid.replace('@', '%') + '@' + transport
1064 self._add_jid(jid, type_)
1065 else:
1066 self._add_jid(jid, type_)
1068 def _add_jid(self, jid, type_):
1069 # check if jid is conform to RFC and stringprep it
1070 try:
1071 jid = helpers.parse_jid(jid)
1072 except helpers.InvalidFormat, s:
1073 pritext = _('Invalid User ID')
1074 ErrorDialog(pritext, str(s))
1075 return
1077 # No resource in jid
1078 if jid.find('/') >= 0:
1079 pritext = _('Invalid User ID')
1080 ErrorDialog(pritext, _('The user ID must not contain a resource.'))
1081 return
1083 if jid == gajim.get_jid_from_account(self.account):
1084 pritext = _('Invalid User ID')
1085 ErrorDialog(pritext, _('You cannot add yourself to your roster.'))
1086 return
1088 nickname = self.nickname_entry.get_text().decode('utf-8') or ''
1089 # get value of account combobox, if account was not specified
1090 if not self.account:
1091 model = self.account_combobox.get_model()
1092 index = self.account_combobox.get_active()
1093 self.account = model[index][1]
1095 # Check if jid is already in roster
1096 if jid in gajim.contacts.get_jid_list(self.account):
1097 c = gajim.contacts.get_first_contact_from_jid(self.account, jid)
1098 if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
1099 ErrorDialog(_('Contact already in roster'),
1100 _('This contact is already listed in your roster.'))
1101 return
1103 if type_ == 'jabber':
1104 message_buffer = self.message_textview.get_buffer()
1105 start_iter = message_buffer.get_start_iter()
1106 end_iter = message_buffer.get_end_iter()
1107 message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
1108 if self.save_message_checkbutton.get_active():
1109 gajim.config.set_per('accounts', self.account,
1110 'subscription_request_msg', message)
1111 else:
1112 message= ''
1113 group = self.group_comboboxentry.child.get_text().decode('utf-8')
1114 groups = []
1115 if group:
1116 groups = [group]
1117 auto_auth = self.auto_authorize_checkbutton.get_active()
1118 gajim.interface.roster.req_sub(self, jid, message, self.account,
1119 groups=groups, nickname=nickname, auto_auth=auto_auth)
1120 self.window.destroy()
1122 def on_account_combobox_changed(self, widget):
1123 model = widget.get_model()
1124 iter_ = widget.get_active_iter()
1125 account = model[iter_][0]
1126 message_buffer = self.message_textview.get_buffer()
1127 message_buffer.set_text(helpers.get_subscription_request_msg(account))
1129 def on_protocol_jid_combobox_changed(self, widget):
1130 model = widget.get_model()
1131 iter_ = widget.get_active_iter()
1132 if not iter_:
1133 return
1134 jid_ = model[iter_][0]
1135 model = self.protocol_combobox.get_model()
1136 iter_ = self.protocol_combobox.get_active_iter()
1137 type_ = model[iter_][2]
1138 desc = None
1139 if self.agents[type_] and jid_ in self.gateway_prompt:
1140 desc = self.gateway_prompt[jid_]['desc']
1141 if not desc:
1142 desc = self.default_desc
1143 self.xml.get_object('prompt_label').set_text(desc)
1145 prompt = None
1146 if self.agents[type_] and jid_ in self.gateway_prompt:
1147 prompt = self.gateway_prompt[jid_]['prompt']
1148 if not prompt:
1149 if type_ in self.uid_labels:
1150 prompt = self.uid_labels[type_]
1151 else:
1152 prompt = _('User ID:')
1153 self.uid_label.set_text(prompt)
1155 def on_protocol_combobox_changed(self, widget):
1156 model = widget.get_model()
1157 iter_ = widget.get_active_iter()
1158 type_ = model[iter_][2]
1159 model = self.protocol_jid_combobox.get_model()
1160 model.clear()
1161 if len(self.agents[type_]):
1162 for jid_ in self.agents[type_]:
1163 model.append([jid_])
1164 self.protocol_jid_combobox.set_active(0)
1165 desc = None
1166 if self.agents[type_]:
1167 jid_ = self.agents[type_][0]
1168 if jid_ in self.gateway_prompt:
1169 desc = self.gateway_prompt[jid_]['desc']
1170 if not desc:
1171 desc = self.default_desc
1172 self.xml.get_object('prompt_label').set_text(desc)
1173 if len(self.agents[type_]) > 1:
1174 self.protocol_jid_combobox.show()
1175 else:
1176 self.protocol_jid_combobox.hide()
1177 prompt = None
1178 if self.agents[type_]:
1179 jid_ = self.agents[type_][0]
1180 if jid_ in self.gateway_prompt:
1181 prompt = self.gateway_prompt[jid_]['prompt']
1182 if not prompt:
1183 if type_ in self.uid_labels:
1184 prompt = self.uid_labels[type_]
1185 else:
1186 prompt = _('User ID:')
1187 self.uid_label.set_text(prompt)
1189 if type_ == 'jabber':
1190 self.message_scrolledwindow.show()
1191 self.save_message_checkbutton.show()
1192 else:
1193 self.message_scrolledwindow.hide()
1194 self.save_message_checkbutton.hide()
1195 if type_ in self.available_types:
1196 self.register_hbox.show()
1197 self.auto_authorize_checkbutton.hide()
1198 self.connected_label.hide()
1199 self.subscription_table.hide()
1200 self.add_button.set_sensitive(False)
1201 else:
1202 self.register_hbox.hide()
1203 if type_ != 'jabber':
1204 jid = self.protocol_jid_combobox.get_active_text()
1205 contact = gajim.contacts.get_first_contact_from_jid(
1206 self.account, jid)
1207 if contact.show in ('offline', 'error'):
1208 self.subscription_table.hide()
1209 self.connected_label.show()
1210 self.add_button.set_sensitive(False)
1211 self.auto_authorize_checkbutton.hide()
1212 return
1213 self.subscription_table.show()
1214 self.auto_authorize_checkbutton.show()
1215 self.connected_label.hide()
1216 self.add_button.set_sensitive(True)
1218 def transport_signed_in(self, jid):
1219 if self.protocol_jid_combobox.get_active_text() == jid:
1220 self.register_hbox.hide()
1221 self.connected_label.hide()
1222 self.subscription_table.show()
1223 self.auto_authorize_checkbutton.show()
1224 self.add_button.set_sensitive(True)
1226 def transport_signed_out(self, jid):
1227 if self.protocol_jid_combobox.get_active_text() == jid:
1228 self.subscription_table.hide()
1229 self.auto_authorize_checkbutton.hide()
1230 self.connected_label.show()
1231 self.add_button.set_sensitive(False)
1233 def _nec_presence_received(self, obj):
1234 if gajim.jid_is_transport(obj.jid):
1235 if obj.old_show == 0 and obj.new_show > 1:
1236 self.transport_signed_in(obj.jid)
1237 elif obj.old_show > 1 and obj.new_show == 0:
1238 self.transport_signed_out(obj.jid)
1240 def _nec_gateway_prompt_received(self, obj):
1241 if self.adding_jid:
1242 jid, transport, type_ = self.adding_jid
1243 if obj.prompt_jid:
1244 self._add_jid(obj.prompt_jid, type_)
1245 else:
1246 jid = jid.replace('@', '%') + '@' + transport
1247 self._add_jid(jid, type_)
1248 elif obj.jid in self.gateway_prompt:
1249 if obj.desc:
1250 self.gateway_prompt[obj.jid]['desc'] = obj.desc
1251 if obj.prompt:
1252 self.gateway_prompt[obj.jid]['prompt'] = obj.prompt
1254 class AboutDialog:
1256 Class for about dialog
1259 def __init__(self):
1260 dlg = gtk.AboutDialog()
1261 dlg.set_transient_for(gajim.interface.roster.window)
1262 dlg.set_name('Gajim')
1263 dlg.set_version(gajim.version)
1264 s = u'Copyright © 2003-2010 Gajim Team'
1265 dlg.set_copyright(s)
1266 copying_file_path = self.get_path('COPYING')
1267 if copying_file_path:
1268 text = open(copying_file_path).read()
1269 dlg.set_license(text)
1271 dlg.set_comments('%s\n%s %s\n%s %s' % (_('A GTK+ jabber client'),
1272 _('GTK+ Version:'), self.tuple2str(gtk.gtk_version), \
1273 _('PyGTK Version:'), self.tuple2str(gtk.pygtk_version)))
1274 dlg.set_website('http://www.gajim.org/')
1276 authors_file_path = self.get_path('AUTHORS')
1277 if authors_file_path:
1278 authors = []
1279 authors_file = open(authors_file_path).read()
1280 authors_file = authors_file.split('\n')
1281 for author in authors_file:
1282 if author == 'CURRENT DEVELOPERS:':
1283 authors.append(_('Current Developers:'))
1284 elif author == 'PAST DEVELOPERS:':
1285 authors.append('\n' + _('Past Developers:'))
1286 elif author != '': # Real author line
1287 authors.append(author)
1289 thanks_file_path = self.get_path('THANKS')
1290 if thanks_file_path:
1291 authors.append('\n' + _('THANKS:'))
1293 text = open(thanks_file_path).read()
1294 text_splitted = text.split('\n')
1295 text = '\n'.join(text_splitted[:-2]) # remove one english sentence
1296 # and add it manually as translatable
1297 text += '\n%s\n' % _('Last but not least, we would like to thank all '
1298 'the package maintainers.')
1299 authors.append(text)
1301 dlg.set_authors(authors)
1303 dlg.props.wrap_license = True
1305 pixbuf = gtkgui_helpers.get_icon_pixmap('gajim-about', 128)
1307 dlg.set_logo(pixbuf)
1308 #here you write your name in the form Name FamilyName <someone@somewhere>
1309 dlg.set_translator_credits(_('translator-credits'))
1311 thanks_artists_file_path = self.get_path('THANKS.artists')
1312 if thanks_artists_file_path:
1313 artists_text = open(thanks_artists_file_path).read()
1314 artists = artists_text.split('\n')
1315 dlg.set_artists(artists)
1316 # connect close button to destroy() function
1317 for button in dlg.action_area.get_children():
1318 if button.get_property('label') == gtk.STOCK_CLOSE:
1319 button.connect('clicked', lambda x:dlg.destroy())
1320 dlg.show_all()
1322 def tuple2str(self, tuple_):
1323 str_ = ''
1324 for num in tuple_:
1325 str_ += str(num) + '.'
1326 return str_[0:-1] # remove latest .
1328 def get_path(self, filename):
1330 Where can we find this Credits file?
1332 if os.path.isfile(os.path.join(gajim.defs.docdir, filename)):
1333 return os.path.join(gajim.defs.docdir, filename)
1334 elif os.path.isfile('../' + filename):
1335 return ('../' + filename)
1336 else:
1337 return None
1339 class Dialog(gtk.Dialog):
1340 def __init__(self, parent, title, buttons, default=None,
1341 on_response_ok=None, on_response_cancel=None):
1342 gtk.Dialog.__init__(self, title, parent,
1343 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR)
1345 self.user_response_ok = on_response_ok
1346 self.user_response_cancel = on_response_cancel
1347 self.set_border_width(6)
1348 self.vbox.set_spacing(12)
1349 self.set_resizable(False)
1351 possible_responses = {gtk.STOCK_OK: self.on_response_ok,
1352 gtk.STOCK_CANCEL: self.on_response_cancel}
1353 for stock, response in buttons:
1354 b = self.add_button(stock, response)
1355 for response in possible_responses:
1356 if stock == response:
1357 b.connect('clicked', possible_responses[response])
1358 break
1360 if default is not None:
1361 self.set_default_response(default)
1362 else:
1363 self.set_default_response(buttons[-1][1])
1365 def on_response_ok(self, widget):
1366 if self.user_response_ok:
1367 if isinstance(self.user_response_ok, tuple):
1368 self.user_response_ok[0](*self.user_response_ok[1:])
1369 else:
1370 self.user_response_ok()
1371 self.destroy()
1373 def on_response_cancel(self, widget):
1374 if self.user_response_cancel:
1375 if isinstance(self.user_response_cancel, tuple):
1376 self.user_response_cancel[0](*self.user_response_ok[1:])
1377 else:
1378 self.user_response_cancel()
1379 self.destroy()
1381 def just_destroy(self, widget):
1382 self.destroy()
1384 def get_button(self, index):
1385 buttons = self.action_area.get_children()
1386 return index < len(buttons) and buttons[index] or None
1389 class HigDialog(gtk.MessageDialog):
1390 def __init__(self, parent, type_, buttons, pritext, sectext,
1391 on_response_ok=None, on_response_cancel=None, on_response_yes=None,
1392 on_response_no=None):
1393 self.call_cancel_on_destroy = True
1394 gtk.MessageDialog.__init__(self, parent,
1395 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
1396 type_, buttons, message_format = pritext)
1398 self.format_secondary_markup(sectext)
1400 buttons = self.action_area.get_children()
1401 self.possible_responses = {gtk.STOCK_OK: on_response_ok,
1402 gtk.STOCK_CANCEL: on_response_cancel, gtk.STOCK_YES: on_response_yes,
1403 gtk.STOCK_NO: on_response_no}
1404 for b in buttons:
1405 for response in self.possible_responses:
1406 if b.get_label() == response:
1407 if not self.possible_responses[response]:
1408 b.connect('clicked', self.just_destroy)
1409 elif isinstance(self.possible_responses[response], tuple):
1410 if len(self.possible_responses[response]) == 1:
1411 b.connect('clicked', self.possible_responses[response][0])
1412 else:
1413 b.connect('clicked', *self.possible_responses[response])
1414 else:
1415 b.connect('clicked', self.possible_responses[response])
1416 break
1418 self.connect('destroy', self.on_dialog_destroy)
1420 def on_dialog_destroy(self, widget):
1421 if not self.call_cancel_on_destroy:
1422 return
1423 cancel_handler = self.possible_responses[gtk.STOCK_CANCEL]
1424 if not cancel_handler:
1425 return False
1426 if isinstance(cancel_handler, tuple):
1427 cancel_handler[0](None, *cancel_handler[1:])
1428 else:
1429 cancel_handler(None)
1431 def just_destroy(self, widget):
1432 self.destroy()
1434 def popup(self):
1436 Show dialog
1438 vb = self.get_children()[0].get_children()[0] # Give focus to top vbox
1439 vb.set_flags(gtk.CAN_FOCUS)
1440 vb.grab_focus()
1441 self.show_all()
1443 class FileChooserDialog(gtk.FileChooserDialog):
1445 Non-blocking FileChooser Dialog around gtk.FileChooserDialog
1447 def __init__(self, title_text, action, buttons, default_response,
1448 select_multiple=False, current_folder=None, on_response_ok=None,
1449 on_response_cancel=None):
1451 gtk.FileChooserDialog.__init__(self, title=title_text, action=action,
1452 buttons=buttons)
1454 self.set_default_response(default_response)
1455 self.set_select_multiple(select_multiple)
1456 if current_folder and os.path.isdir(current_folder):
1457 self.set_current_folder(current_folder)
1458 else:
1459 self.set_current_folder(helpers.get_documents_path())
1460 self.response_ok, self.response_cancel = \
1461 on_response_ok, on_response_cancel
1462 # in gtk+-2.10 clicked signal on some of the buttons in a dialog
1463 # is emitted twice, so we cannot rely on 'clicked' signal
1464 self.connect('response', self.on_dialog_response)
1465 self.show_all()
1467 def on_dialog_response(self, dialog, response):
1468 if response in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_CLOSE):
1469 if self.response_cancel:
1470 if isinstance(self.response_cancel, tuple):
1471 self.response_cancel[0](dialog, *self.response_cancel[1:])
1472 else:
1473 self.response_cancel(dialog)
1474 else:
1475 self.just_destroy(dialog)
1476 elif response == gtk.RESPONSE_OK:
1477 if self.response_ok:
1478 if isinstance(self.response_ok, tuple):
1479 self.response_ok[0](dialog, *self.response_ok[1:])
1480 else:
1481 self.response_ok(dialog)
1482 else:
1483 self.just_destroy(dialog)
1485 def just_destroy(self, widget):
1486 self.destroy()
1488 class AspellDictError:
1489 def __init__(self, lang):
1490 ErrorDialog(
1491 _('Dictionary for lang %s not available') % lang,
1492 _('You have to install %s dictionary to use spellchecking, or '
1493 'choose another language by setting the speller_language option.'
1494 '\n\nHighlighting misspelled words feature will not be used') % lang)
1495 gajim.config.set('use_speller', False)
1497 class ConfirmationDialog(HigDialog):
1499 HIG compliant confirmation dialog
1502 def __init__(self, pritext, sectext='', on_response_ok=None,
1503 on_response_cancel=None):
1504 self.user_response_ok = on_response_ok
1505 self.user_response_cancel = on_response_cancel
1506 HigDialog.__init__(self, None,
1507 gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
1508 self.on_response_ok, self.on_response_cancel)
1509 self.popup()
1511 def on_response_ok(self, widget):
1512 if self.user_response_ok:
1513 if isinstance(self.user_response_ok, tuple):
1514 self.user_response_ok[0](*self.user_response_ok[1:])
1515 else:
1516 self.user_response_ok()
1517 self.call_cancel_on_destroy = False
1518 self.destroy()
1520 def on_response_cancel(self, widget):
1521 if self.user_response_cancel:
1522 if isinstance(self.user_response_cancel, tuple):
1523 self.user_response_cancel[0](*self.user_response_ok[1:])
1524 else:
1525 self.user_response_cancel()
1526 self.call_cancel_on_destroy = False
1527 self.destroy()
1529 class NonModalConfirmationDialog(HigDialog):
1531 HIG compliant non modal confirmation dialog
1534 def __init__(self, pritext, sectext='', on_response_ok=None,
1535 on_response_cancel=None):
1536 self.user_response_ok = on_response_ok
1537 self.user_response_cancel = on_response_cancel
1538 HigDialog.__init__(self, None,
1539 gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
1540 self.on_response_ok, self.on_response_cancel)
1541 self.set_modal(False)
1543 def on_response_ok(self, widget):
1544 if self.user_response_ok:
1545 if isinstance(self.user_response_ok, tuple):
1546 self.user_response_ok[0](*self.user_response_ok[1:])
1547 else:
1548 self.user_response_ok()
1549 self.call_cancel_on_destroy = False
1550 self.destroy()
1552 def on_response_cancel(self, widget):
1553 if self.user_response_cancel:
1554 if isinstance(self.user_response_cancel, tuple):
1555 self.user_response_cancel[0](*self.user_response_cancel[1:])
1556 else:
1557 self.user_response_cancel()
1558 self.call_cancel_on_destroy = False
1559 self.destroy()
1561 class WarningDialog(HigDialog):
1563 HIG compliant warning dialog
1566 def __init__(self, pritext, sectext='', transient_for=None):
1567 HigDialog.__init__(self, None, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
1568 pritext, sectext)
1569 self.set_modal(False)
1570 if transient_for is None and hasattr(gajim.interface, 'roster') and \
1571 gajim.interface.roster:
1572 transient_for = gajim.interface.roster.window
1573 if transient_for:
1574 self.set_transient_for(transient_for)
1575 self.popup()
1577 class InformationDialog(HigDialog):
1579 HIG compliant info dialog
1582 def __init__(self, pritext, sectext=''):
1583 HigDialog.__init__(self, None, gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
1584 pritext, sectext)
1585 self.set_modal(False)
1586 self.set_transient_for(gajim.interface.roster.window)
1587 self.popup()
1589 class ErrorDialog(HigDialog):
1591 HIG compliant error dialog
1594 def __init__(self, pritext, sectext='', on_response_ok=None,
1595 on_response_cancel=None):
1596 HigDialog.__init__( self, None, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
1597 pritext, sectext, on_response_ok=on_response_ok,
1598 on_response_cancel=on_response_cancel)
1599 self.popup()
1601 class YesNoDialog(HigDialog):
1603 HIG compliant YesNo dialog
1606 def __init__(self, pritext, sectext='', checktext='', on_response_yes=None,
1607 on_response_no=None):
1608 self.user_response_yes = on_response_yes
1609 self.user_response_no = on_response_no
1610 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
1611 pritext, sectext, on_response_yes=self.on_response_yes,
1612 on_response_no=self.on_response_no)
1614 if checktext:
1615 self.checkbutton = gtk.CheckButton(checktext)
1616 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1617 else:
1618 self.checkbutton = None
1619 self.set_modal(False)
1620 self.popup()
1622 def on_response_yes(self, widget):
1623 if self.user_response_yes:
1624 if isinstance(self.user_response_yes, tuple):
1625 self.user_response_yes[0](self.is_checked(),
1626 *self.user_response_yes[1:])
1627 else:
1628 self.user_response_yes(self.is_checked())
1629 self.call_cancel_on_destroy = False
1630 self.destroy()
1632 def on_response_no(self, widget):
1633 if self.user_response_no:
1634 if isinstance(self.user_response_no, tuple):
1635 self.user_response_no[0](*self.user_response_no[1:])
1636 else:
1637 self.user_response_no()
1638 self.call_cancel_on_destroy = False
1639 self.destroy()
1641 def is_checked(self):
1643 Get active state of the checkbutton
1645 if not self.checkbutton:
1646 return False
1647 return self.checkbutton.get_active()
1649 class ConfirmationDialogCheck(ConfirmationDialog):
1651 HIG compliant confirmation dialog with checkbutton
1654 def __init__(self, pritext, sectext='', checktext='', on_response_ok=None,
1655 on_response_cancel=None, is_modal=True):
1656 self.user_response_ok = on_response_ok
1657 self.user_response_cancel = on_response_cancel
1659 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1660 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1661 self.on_response_cancel)
1663 self.set_default_response(gtk.RESPONSE_OK)
1665 ok_button = self.action_area.get_children()[0] # right to left
1666 ok_button.grab_focus()
1668 self.checkbutton = gtk.CheckButton(checktext)
1669 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1670 self.set_modal(is_modal)
1671 self.popup()
1673 def on_response_ok(self, widget):
1674 if self.user_response_ok:
1675 if isinstance(self.user_response_ok, tuple):
1676 self.user_response_ok[0](self.is_checked(),
1677 *self.user_response_ok[1:])
1678 else:
1679 self.user_response_ok(self.is_checked())
1680 self.call_cancel_on_destroy = False
1681 self.destroy()
1683 def on_response_cancel(self, widget):
1684 if self.user_response_cancel:
1685 if isinstance(self.user_response_cancel, tuple):
1686 self.user_response_cancel[0](self.is_checked(),
1687 *self.user_response_cancel[1:])
1688 else:
1689 self.user_response_cancel(self.is_checked())
1690 self.call_cancel_on_destroy = False
1691 self.destroy()
1693 def is_checked(self):
1695 Get active state of the checkbutton
1697 return self.checkbutton.get_active()
1699 class ConfirmationDialogDoubleCheck(ConfirmationDialog):
1701 HIG compliant confirmation dialog with 2 checkbuttons
1704 def __init__(self, pritext, sectext='', checktext1='', checktext2='',
1705 on_response_ok=None, on_response_cancel=None, is_modal=True):
1706 self.user_response_ok = on_response_ok
1707 self.user_response_cancel = on_response_cancel
1709 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1710 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1711 self.on_response_cancel)
1713 self.set_default_response(gtk.RESPONSE_OK)
1715 ok_button = self.action_area.get_children()[0] # right to left
1716 ok_button.grab_focus()
1718 if checktext1:
1719 self.checkbutton1 = gtk.CheckButton(checktext1)
1720 self.vbox.pack_start(self.checkbutton1, expand=False, fill=True)
1721 else:
1722 self.checkbutton1 = None
1723 if checktext2:
1724 self.checkbutton2 = gtk.CheckButton(checktext2)
1725 self.vbox.pack_start(self.checkbutton2, expand=False, fill=True)
1726 else:
1727 self.checkbutton2 = None
1729 self.set_modal(is_modal)
1730 self.popup()
1732 def on_response_ok(self, widget):
1733 if self.user_response_ok:
1734 if isinstance(self.user_response_ok, tuple):
1735 self.user_response_ok[0](self.is_checked(),
1736 *self.user_response_ok[1:])
1737 else:
1738 self.user_response_ok(self.is_checked())
1739 self.call_cancel_on_destroy = False
1740 self.destroy()
1742 def on_response_cancel(self, widget):
1743 if self.user_response_cancel:
1744 if isinstance(self.user_response_cancel, tuple):
1745 self.user_response_cancel[0](*self.user_response_cancel[1:])
1746 else:
1747 self.user_response_cancel()
1748 self.call_cancel_on_destroy = False
1749 self.destroy()
1751 def is_checked(self):
1752 ''' Get active state of the checkbutton '''
1753 if self.checkbutton1:
1754 is_checked_1 = self.checkbutton1.get_active()
1755 else:
1756 is_checked_1 = False
1757 if self.checkbutton2:
1758 is_checked_2 = self.checkbutton2.get_active()
1759 else:
1760 is_checked_2 = False
1761 return [is_checked_1, is_checked_2]
1763 class ConfirmationDialogDoubleRadio(ConfirmationDialog):
1765 HIG compliant confirmation dialog with 2 radios
1768 def __init__(self, pritext, sectext='', radiotext1='', radiotext2='',
1769 on_response_ok=None, on_response_cancel=None, is_modal=True):
1770 self.user_response_ok = on_response_ok
1771 self.user_response_cancel = on_response_cancel
1773 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION,
1774 gtk.BUTTONS_OK_CANCEL, pritext, sectext, self.on_response_ok,
1775 self.on_response_cancel)
1777 self.set_default_response(gtk.RESPONSE_OK)
1779 ok_button = self.action_area.get_children()[0] # right to left
1780 ok_button.grab_focus()
1782 self.radiobutton1 = gtk.RadioButton(label=radiotext1)
1783 self.vbox.pack_start(self.radiobutton1, expand=False, fill=True)
1785 self.radiobutton2 = gtk.RadioButton(group=self.radiobutton1,
1786 label=radiotext2)
1787 self.vbox.pack_start(self.radiobutton2, expand=False, fill=True)
1789 self.set_modal(is_modal)
1790 self.popup()
1792 def on_response_ok(self, widget):
1793 if self.user_response_ok:
1794 if isinstance(self.user_response_ok, tuple):
1795 self.user_response_ok[0](self.is_checked(),
1796 *self.user_response_ok[1:])
1797 else:
1798 self.user_response_ok(self.is_checked())
1799 self.call_cancel_on_destroy = False
1800 self.destroy()
1802 def on_response_cancel(self, widget):
1803 if self.user_response_cancel:
1804 if isinstance(self.user_response_cancel, tuple):
1805 self.user_response_cancel[0](*self.user_response_cancel[1:])
1806 else:
1807 self.user_response_cancel()
1808 self.call_cancel_on_destroy = False
1809 self.destroy()
1811 def is_checked(self):
1812 ''' Get active state of the checkbutton '''
1813 if self.radiobutton1:
1814 is_checked_1 = self.radiobutton1.get_active()
1815 else:
1816 is_checked_1 = False
1817 if self.radiobutton2:
1818 is_checked_2 = self.radiobutton2.get_active()
1819 else:
1820 is_checked_2 = False
1821 return [is_checked_1, is_checked_2]
1823 class FTOverwriteConfirmationDialog(ConfirmationDialog):
1825 HIG compliant confirmation dialog to overwrite or resume a file transfert
1828 def __init__(self, pritext, sectext='', propose_resume=True,
1829 on_response=None):
1830 HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_CANCEL,
1831 pritext, sectext)
1833 self.on_response = on_response
1835 if propose_resume:
1836 b = gtk.Button('', gtk.STOCK_REFRESH)
1837 align = b.get_children()[0]
1838 hbox = align.get_children()[0]
1839 label = hbox.get_children()[1]
1840 label.set_text(_('_Resume'))
1841 label.set_use_underline(True)
1842 self.add_action_widget(b, 100)
1844 b = gtk.Button('', gtk.STOCK_SAVE_AS)
1845 align = b.get_children()[0]
1846 hbox = align.get_children()[0]
1847 label = hbox.get_children()[1]
1848 label.set_text(_('Re_place'))
1849 label.set_use_underline(True)
1850 self.add_action_widget(b, 200)
1852 self.connect('response', self.on_dialog_response)
1853 self.show_all()
1855 def on_dialog_response(self, dialog, response):
1856 if self.on_response:
1857 if isinstance(self.on_response, tuple):
1858 self.on_response[0](response, *self.on_response[1:])
1859 else:
1860 self.on_response(response)
1861 self.call_cancel_on_destroy = False
1862 self.destroy()
1864 class CommonInputDialog:
1866 Common Class for Input dialogs
1869 def __init__(self, title, label_str, is_modal, ok_handler, cancel_handler):
1870 self.dialog = self.xml.get_object('input_dialog')
1871 label = self.xml.get_object('label')
1872 self.dialog.set_title(title)
1873 label.set_markup(label_str)
1874 self.cancel_handler = cancel_handler
1875 self.vbox = self.xml.get_object('vbox')
1877 self.ok_handler = ok_handler
1878 okbutton = self.xml.get_object('okbutton')
1879 okbutton.connect('clicked', self.on_okbutton_clicked)
1880 cancelbutton = self.xml.get_object('cancelbutton')
1881 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
1882 self.xml.connect_signals(self)
1883 self.dialog.show_all()
1885 def on_input_dialog_destroy(self, widget):
1886 if self.cancel_handler:
1887 self.cancel_handler()
1889 def on_okbutton_clicked(self, widget):
1890 user_input = self.get_text()
1891 if user_input:
1892 user_input = user_input.decode('utf-8')
1893 self.cancel_handler = None
1894 self.dialog.destroy()
1895 if isinstance(self.ok_handler, tuple):
1896 self.ok_handler[0](user_input, *self.ok_handler[1:])
1897 else:
1898 self.ok_handler(user_input)
1900 def on_cancelbutton_clicked(self, widget):
1901 self.dialog.destroy()
1903 class InputDialog(CommonInputDialog):
1905 Class for Input dialog
1908 def __init__(self, title, label_str, input_str=None, is_modal=True,
1909 ok_handler=None, cancel_handler=None):
1910 self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
1911 CommonInputDialog.__init__(self, title, label_str, is_modal, ok_handler,
1912 cancel_handler)
1913 self.input_entry = self.xml.get_object('input_entry')
1914 if input_str:
1915 self.set_entry(input_str)
1917 def on_input_dialog_delete_event(self, widget, event):
1919 may be implemented by subclasses
1921 pass
1923 def set_entry(self, value):
1924 self.input_entry.set_text(value)
1925 self.input_entry.select_region(0, -1) # select all
1927 def get_text(self):
1928 return self.input_entry.get_text().decode('utf-8')
1930 class InputDialogCheck(InputDialog):
1932 Class for Input dialog
1935 def __init__(self, title, label_str, checktext='', input_str=None,
1936 is_modal=True, ok_handler=None, cancel_handler=None):
1937 self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
1938 InputDialog.__init__(self, title, label_str, input_str=input_str,
1939 is_modal=is_modal, ok_handler=ok_handler,
1940 cancel_handler=cancel_handler)
1941 self.input_entry = self.xml.get_object('input_entry')
1942 if input_str:
1943 self.input_entry.set_text(input_str)
1944 self.input_entry.select_region(0, -1) # select all
1946 if checktext:
1947 self.checkbutton = gtk.CheckButton(checktext)
1948 self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
1949 self.checkbutton.show()
1951 def on_okbutton_clicked(self, widget):
1952 user_input = self.get_text()
1953 if user_input:
1954 user_input = user_input.decode('utf-8')
1955 self.cancel_handler = None
1956 self.dialog.destroy()
1957 if isinstance(self.ok_handler, tuple):
1958 self.ok_handler[0](user_input, self.is_checked(), *self.ok_handler[1:])
1959 else:
1960 self.ok_handler(user_input, self.is_checked())
1962 def get_text(self):
1963 return self.input_entry.get_text().decode('utf-8')
1965 def is_checked(self):
1967 Get active state of the checkbutton
1969 try:
1970 return self.checkbutton.get_active()
1971 except Exception:
1972 # There is no checkbutton
1973 return False
1975 class ChangeNickDialog(InputDialogCheck):
1977 Class for changing room nickname in case of conflict
1980 def __init__(self, account, room_jid, title, prompt, check_text=None,
1981 change_nick=False):
1983 change_nick must be set to True when we are already occupant of the room
1984 and we are changing our nick
1986 InputDialogCheck.__init__(self, title, '', checktext=check_text,
1987 input_str='', is_modal=True, ok_handler=None, cancel_handler=None)
1988 self.room_queue = [(account, room_jid, prompt, change_nick)]
1989 self.check_next()
1991 def on_input_dialog_delete_event(self, widget, event):
1992 self.on_cancelbutton_clicked(widget)
1993 return True
1995 def setup_dialog(self):
1996 self.gc_control = gajim.interface.msg_win_mgr.get_gc_control(
1997 self.room_jid, self.account)
1998 if not self.gc_control and \
1999 self.room_jid in gajim.interface.minimized_controls[self.account]:
2000 self.gc_control = \
2001 gajim.interface.minimized_controls[self.account][self.room_jid]
2002 if not self.gc_control:
2003 self.check_next()
2004 return
2005 label = self.xml.get_object('label')
2006 label.set_markup(self.prompt)
2007 self.set_entry(self.gc_control.nick + \
2008 gajim.config.get('gc_proposed_nick_char'))
2010 def check_next(self):
2011 if len(self.room_queue) == 0:
2012 self.cancel_handler = None
2013 self.dialog.destroy()
2014 if 'change_nick_dialog' in gajim.interface.instances:
2015 del gajim.interface.instances['change_nick_dialog']
2016 return
2017 self.account, self.room_jid, self.prompt, self.change_nick = \
2018 self.room_queue.pop(0)
2019 self.setup_dialog()
2021 if gajim.new_room_nick is not None and not gajim.gc_connected[
2022 self.account][self.room_jid] and self.gc_control.nick != \
2023 gajim.new_room_nick:
2024 self.dialog.hide()
2025 self.on_ok(gajim.new_room_nick, True)
2026 else:
2027 self.dialog.show()
2029 def on_okbutton_clicked(self, widget):
2030 nick = self.get_text()
2031 if nick:
2032 nick = nick.decode('utf-8')
2033 # send presence to room
2034 try:
2035 nick = helpers.parse_resource(nick)
2036 except Exception:
2037 # invalid char
2038 ErrorDialog(_('Invalid nickname'),
2039 _('The nickname has not allowed characters.'))
2040 return
2041 self.on_ok(nick, self.is_checked())
2043 def on_ok(self, nick, is_checked):
2044 if is_checked:
2045 gajim.new_room_nick = nick
2046 gajim.connections[self.account].join_gc(nick, self.room_jid, None,
2047 change_nick=self.change_nick)
2048 if gajim.gc_connected[self.account][self.room_jid]:
2049 # We are changing nick, we will change self.nick when we receive
2050 # presence that inform that it works
2051 self.gc_control.new_nick = nick
2052 else:
2053 # We are connecting, we will not get a changed nick presence so
2054 # change it NOW. We don't already have a nick so it's harmless
2055 self.gc_control.nick = nick
2056 self.check_next()
2058 def on_cancelbutton_clicked(self, widget):
2059 self.gc_control.new_nick = ''
2060 self.check_next()
2062 def add_room(self, account, room_jid, prompt, change_nick=False):
2063 if (account, room_jid, prompt, change_nick) not in self.room_queue:
2064 self.room_queue.append((account, room_jid, prompt, change_nick))
2066 class InputTextDialog(CommonInputDialog):
2068 Class for multilines Input dialog (more place than InputDialog)
2071 def __init__(self, title, label_str, input_str=None, is_modal=True,
2072 ok_handler=None, cancel_handler=None):
2073 self.xml = gtkgui_helpers.get_gtk_builder('input_text_dialog.ui')
2074 CommonInputDialog.__init__(self, title, label_str, is_modal, ok_handler,
2075 cancel_handler)
2076 self.input_buffer = self.xml.get_object('input_textview').get_buffer()
2077 if input_str:
2078 self.input_buffer.set_text(input_str)
2079 start_iter, end_iter = self.input_buffer.get_bounds()
2080 self.input_buffer.select_range(start_iter, end_iter) # select all
2082 def get_text(self):
2083 start_iter, end_iter = self.input_buffer.get_bounds()
2084 return self.input_buffer.get_text(start_iter, end_iter).decode('utf-8')
2086 class DoubleInputDialog:
2088 Class for Double Input dialog
2091 def __init__(self, title, label_str1, label_str2, input_str1=None,
2092 input_str2=None, is_modal=True, ok_handler=None, cancel_handler=None):
2093 self.xml = gtkgui_helpers.get_gtk_builder('dubbleinput_dialog.ui')
2094 self.dialog = self.xml.get_object('dubbleinput_dialog')
2095 label1 = self.xml.get_object('label1')
2096 self.input_entry1 = self.xml.get_object('input_entry1')
2097 label2 = self.xml.get_object('label2')
2098 self.input_entry2 = self.xml.get_object('input_entry2')
2099 self.dialog.set_title(title)
2100 label1.set_markup(label_str1)
2101 label2.set_markup(label_str2)
2102 self.cancel_handler = cancel_handler
2103 if input_str1:
2104 self.input_entry1.set_text(input_str1)
2105 self.input_entry1.select_region(0, -1) # select all
2106 if input_str2:
2107 self.input_entry2.set_text(input_str2)
2108 self.input_entry2.select_region(0, -1) # select all
2110 self.dialog.set_modal(is_modal)
2112 self.ok_handler = ok_handler
2113 okbutton = self.xml.get_object('okbutton')
2114 okbutton.connect('clicked', self.on_okbutton_clicked)
2115 cancelbutton = self.xml.get_object('cancelbutton')
2116 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
2117 self.xml.connect_signals(self)
2118 self.dialog.show_all()
2120 def on_dubbleinput_dialog_destroy(self, widget):
2121 if not self.cancel_handler:
2122 return False
2123 if isinstance(self.cancel_handler, tuple):
2124 self.cancel_handler[0](*self.cancel_handler[1:])
2125 else:
2126 self.cancel_handler()
2128 def on_okbutton_clicked(self, widget):
2129 user_input1 = self.input_entry1.get_text().decode('utf-8')
2130 user_input2 = self.input_entry2.get_text().decode('utf-8')
2131 self.dialog.destroy()
2132 if not self.ok_handler:
2133 return
2134 if isinstance(self.ok_handler, tuple):
2135 self.ok_handler[0](user_input1, user_input2, *self.ok_handler[1:])
2136 else:
2137 self.ok_handler(user_input1, user_input2)
2139 def on_cancelbutton_clicked(self, widget):
2140 self.dialog.destroy()
2141 if not self.cancel_handler:
2142 return
2143 if isinstance(self.cancel_handler, tuple):
2144 self.cancel_handler[0](*self.cancel_handler[1:])
2145 else:
2146 self.cancel_handler()
2148 class SubscriptionRequestWindow:
2149 def __init__(self, jid, text, account, user_nick=None):
2150 xml = gtkgui_helpers.get_gtk_builder('subscription_request_window.ui')
2151 self.window = xml.get_object('subscription_request_window')
2152 self.jid = jid
2153 self.account = account
2154 self.user_nick = user_nick
2155 if len(gajim.connections) >= 2:
2156 prompt_text = \
2157 _('Subscription request for account %(account)s from %(jid)s')\
2158 % {'account': account, 'jid': self.jid}
2159 else:
2160 prompt_text = _('Subscription request from %s') % self.jid
2161 xml.get_object('from_label').set_text(prompt_text)
2162 xml.get_object('message_textview').get_buffer().set_text(text)
2163 xml.connect_signals(self)
2164 self.window.show_all()
2166 def prepare_popup_menu(self):
2167 xml = gtkgui_helpers.get_gtk_builder('subscription_request_popup_menu.ui')
2168 menu = xml.get_object('subscription_request_popup_menu')
2169 xml.connect_signals(self)
2170 return menu
2172 def on_close_button_clicked(self, widget):
2173 self.window.destroy()
2175 def on_authorize_button_clicked(self, widget):
2177 Accept the request
2179 gajim.connections[self.account].send_authorization(self.jid)
2180 self.window.destroy()
2181 contact = gajim.contacts.get_contact(self.account, self.jid)
2182 if not contact or _('Not in Roster') in contact.groups:
2183 AddNewContactWindow(self.account, self.jid, self.user_nick)
2185 def on_contact_info_activate(self, widget):
2187 Ask vcard
2189 if self.jid in gajim.interface.instances[self.account]['infos']:
2190 gajim.interface.instances[self.account]['infos'][self.jid].window.present()
2191 else:
2192 contact = gajim.contacts.create_contact(jid=self.jid, account=self.account)
2193 gajim.interface.instances[self.account]['infos'][self.jid] = \
2194 vcard.VcardWindow(contact, self.account)
2195 # Remove jabber page
2196 gajim.interface.instances[self.account]['infos'][self.jid].xml.\
2197 get_object('information_notebook').remove_page(0)
2199 def on_start_chat_activate(self, widget):
2201 Open chat
2203 gajim.interface.new_chat_from_jid(self.account, self.jid)
2205 def on_deny_button_clicked(self, widget):
2207 Refuse the request
2209 gajim.connections[self.account].refuse_authorization(self.jid)
2210 contact = gajim.contacts.get_contact(self.account, self.jid)
2211 if contact and _('Not in Roster') in contact.get_shown_groups():
2212 gajim.interface.roster.remove_contact(self.jid, self.account)
2213 self.window.destroy()
2215 def on_actions_button_clicked(self, widget):
2217 Popup action menu
2219 menu = self.prepare_popup_menu()
2220 menu.show_all()
2221 gtkgui_helpers.popup_emoticons_under_button(menu, widget,
2222 self.window.window)
2225 class JoinGroupchatWindow:
2226 def __init__(self, account=None, room_jid='', nick='', password='',
2227 automatic=False):
2229 Automatic is a dict like {'invities': []}. If automatic is not empty,
2230 this means room must be automaticaly configured and when done, invities
2231 must be automatically invited
2233 if account:
2234 if room_jid != '' and room_jid in gajim.gc_connected[account] and\
2235 gajim.gc_connected[account][room_jid]:
2236 ErrorDialog(_('You are already in group chat %s') % room_jid)
2237 raise GajimGeneralException, 'You are already in this group chat'
2238 if nick == '':
2239 nick = gajim.nicks[account]
2240 if gajim.connections[account].connected < 2:
2241 ErrorDialog(_('You are not connected to the server'),
2242 _('You can not join a group chat unless you are connected.'))
2243 raise GajimGeneralException, 'You must be connected to join a groupchat'
2245 self.xml = gtkgui_helpers.get_gtk_builder('join_groupchat_window.ui')
2247 account_label = self.xml.get_object('account_label')
2248 account_combobox = self.xml.get_object('account_combobox')
2249 account_label.set_no_show_all(False)
2250 account_combobox.set_no_show_all(False)
2251 liststore = gtk.ListStore(str)
2252 account_combobox.set_model(liststore)
2253 cell = gtk.CellRendererText()
2254 account_combobox.pack_start(cell, True)
2255 account_combobox.add_attribute(cell, 'text', 0)
2256 account_combobox.set_active(-1)
2258 # Add accounts, set current as active if it matches 'account'
2259 for acct in [a for a in gajim.connections if \
2260 gajim.account_is_connected(a)]:
2261 if gajim.connections[acct].is_zeroconf:
2262 continue
2263 account_combobox.append_text(acct)
2264 if account and account == acct:
2265 account_combobox.set_active(liststore.iter_n_children(None)-1)
2267 self.account = account
2268 self.automatic = automatic
2269 self._empty_required_widgets = []
2271 self.window = self.xml.get_object('join_groupchat_window')
2272 self.window.set_transient_for(gajim.interface.roster.window)
2273 self._room_jid_entry = self.xml.get_object('room_jid_entry')
2274 self._nickname_entry = self.xml.get_object('nickname_entry')
2275 self._password_entry = self.xml.get_object('password_entry')
2277 self._nickname_entry.set_text(nick)
2278 if password:
2279 self._password_entry.set_text(password)
2280 self.xml.connect_signals(self)
2281 title = None
2282 if account:
2283 # now add us to open windows
2284 gajim.interface.instances[account]['join_gc'] = self
2285 if len(gajim.connections) > 1:
2286 title = _('Join Group Chat with account %s') % account
2287 if title is None:
2288 title = _('Join Group Chat')
2289 self.window.set_title(title)
2291 self.server_comboboxentry = self.xml.get_object('server_comboboxentry')
2292 self.server_model = self.server_comboboxentry.get_model()
2293 server_list = []
2294 # get the muc server of our server
2295 if 'jabber' in gajim.connections[account].muc_jid:
2296 server_list.append(gajim.connections[account].muc_jid['jabber'])
2298 self.recently_combobox = self.xml.get_object('recently_combobox')
2299 liststore = gtk.ListStore(str)
2300 self.recently_combobox.set_model(liststore)
2301 cell = gtk.CellRendererText()
2302 self.recently_combobox.pack_start(cell, True)
2303 self.recently_combobox.add_attribute(cell, 'text', 0)
2304 self.recently_groupchat = gajim.config.get('recently_groupchat').split()
2305 for g in self.recently_groupchat:
2306 self.recently_combobox.append_text(g)
2307 server = gajim.get_server_from_jid(g)
2308 if server not in server_list and not server.startswith('irc'):
2309 server_list.append(server)
2311 for s in server_list:
2312 self.server_model.append([s])
2313 self.server_comboboxentry.set_active(0)
2315 self._set_room_jid(room_jid)
2317 if len(self.recently_groupchat) == 0:
2318 self.recently_combobox.set_sensitive(False)
2319 elif room_jid == '':
2320 self.recently_combobox.set_active(0)
2321 self._room_jid_entry.select_region(0, -1)
2322 elif room_jid != '':
2323 self.xml.get_object('join_button').grab_focus()
2325 if not self._room_jid_entry.get_text():
2326 self._empty_required_widgets.append(self._room_jid_entry)
2327 if not self._nickname_entry.get_text():
2328 self._empty_required_widgets.append(self._nickname_entry)
2329 if len(self._empty_required_widgets):
2330 self.xml.get_object('join_button').set_sensitive(False)
2332 if account and not gajim.connections[account].private_storage_supported:
2333 self.xml.get_object('bookmark_checkbutton').set_sensitive(False)
2335 self.window.show_all()
2337 def on_join_groupchat_window_destroy(self, widget):
2339 Close window
2341 if self.account and 'join_gc' in gajim.interface.instances[self.account]:
2342 # remove us from open windows
2343 del gajim.interface.instances[self.account]['join_gc']
2345 def on_join_groupchat_window_key_press_event(self, widget, event):
2346 if event.keyval == gtk.keysyms.Escape: # ESCAPE
2347 widget.destroy()
2349 def on_required_entry_changed(self, widget):
2350 if not widget.get_text():
2351 self._empty_required_widgets.append(widget)
2352 self.xml.get_object('join_button').set_sensitive(False)
2353 else:
2354 if widget in self._empty_required_widgets:
2355 self._empty_required_widgets.remove(widget)
2356 if not self._empty_required_widgets and self.account:
2357 self.xml.get_object('join_button').set_sensitive(True)
2358 text = self._room_jid_entry.get_text()
2359 if widget == self._room_jid_entry and '@' in text:
2360 # Don't allow @ char in room entry
2361 room_jid, server = text.split('@', 1)
2362 self._room_jid_entry.set_text(room_jid)
2363 if server:
2364 self.server_comboboxentry.child.set_text(server)
2365 self.server_comboboxentry.grab_focus()
2367 def on_account_combobox_changed(self, widget):
2368 model = widget.get_model()
2369 iter_ = widget.get_active_iter()
2370 self.account = model[iter_][0].decode('utf-8')
2371 self.on_required_entry_changed(self._nickname_entry)
2373 def _set_room_jid(self, room_jid):
2374 room, server = gajim.get_name_and_server_from_jid(room_jid)
2375 self._room_jid_entry.set_text(room)
2376 self.server_comboboxentry.child.set_text(server)
2378 def on_recently_combobox_changed(self, widget):
2379 model = widget.get_model()
2380 iter_ = widget.get_active_iter()
2381 room_jid = model[iter_][0].decode('utf-8')
2382 self._set_room_jid(room_jid)
2384 def on_browse_rooms_button_clicked(self, widget):
2385 server = self.server_comboboxentry.child.get_text().decode('utf-8')
2386 if server in gajim.interface.instances[self.account]['disco']:
2387 gajim.interface.instances[self.account]['disco'][server].window.\
2388 present()
2389 else:
2390 try:
2391 # Object will add itself to the window dict
2392 import disco
2393 disco.ServiceDiscoveryWindow(self.account, server,
2394 initial_identities=[{'category': 'conference',
2395 'type': 'text'}])
2396 except GajimGeneralException:
2397 pass
2399 def on_cancel_button_clicked(self, widget):
2401 When Cancel button is clicked
2403 self.window.destroy()
2405 def on_bookmark_checkbutton_toggled(self, widget):
2406 auto_join_checkbutton = self.xml.get_object('auto_join_checkbutton')
2407 if widget.get_active():
2408 auto_join_checkbutton.set_sensitive(True)
2409 else:
2410 auto_join_checkbutton.set_sensitive(False)
2412 def on_join_button_clicked(self, widget):
2414 When Join button is clicked
2416 if not self.account:
2417 ErrorDialog(_('Invalid Account'),
2418 _('You have to choose an account from which you want to join the '
2419 'groupchat.'))
2420 return
2421 nickname = self._nickname_entry.get_text().decode('utf-8')
2422 server = self.server_comboboxentry.child.get_text().decode('utf-8')
2423 room = self._room_jid_entry.get_text().decode('utf-8')
2424 room_jid = room + '@' + server
2425 password = self._password_entry.get_text().decode('utf-8')
2426 try:
2427 nickname = helpers.parse_resource(nickname)
2428 except Exception:
2429 ErrorDialog(_('Invalid Nickname'),
2430 _('The nickname has not allowed characters.'))
2431 return
2432 user, server, resource = helpers.decompose_jid(room_jid)
2433 if not user or not server or resource:
2434 ErrorDialog(_('Invalid group chat Jabber ID'),
2435 _('Please enter the group chat Jabber ID as room@server.'))
2436 return
2437 try:
2438 room_jid = helpers.parse_jid(room_jid)
2439 except Exception:
2440 ErrorDialog(_('Invalid group chat Jabber ID'),
2441 _('The group chat Jabber ID has not allowed characters.'))
2442 return
2444 if gajim.interface.msg_win_mgr.has_window(room_jid, self.account):
2445 ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
2446 self.account)
2447 if ctrl.type_id != message_control.TYPE_GC:
2448 ErrorDialog(_('This is not a group chat'),
2449 _('%s is not the name of a group chat.') % room_jid)
2450 return
2451 if room_jid in self.recently_groupchat:
2452 self.recently_groupchat.remove(room_jid)
2453 self.recently_groupchat.insert(0, room_jid)
2454 if len(self.recently_groupchat) > 10:
2455 self.recently_groupchat = self.recently_groupchat[0:10]
2456 gajim.config.set('recently_groupchat',
2457 ' '.join(self.recently_groupchat))
2459 if self.xml.get_object('bookmark_checkbutton').get_active():
2460 if self.xml.get_object('auto_join_checkbutton').get_active():
2461 autojoin = '1'
2462 else:
2463 autojoin = '0'
2464 # Add as bookmark, with autojoin and not minimized
2465 name = gajim.get_nick_from_jid(room_jid)
2466 gajim.interface.add_gc_bookmark(self.account, name, room_jid, autojoin,
2467 '0', password, nickname)
2469 if self.automatic:
2470 gajim.automatic_rooms[self.account][room_jid] = self.automatic
2472 gajim.interface.join_gc_room(self.account, room_jid, nickname, password)
2474 self.window.destroy()
2476 class SynchroniseSelectAccountDialog:
2477 def __init__(self, account):
2478 # 'account' can be None if we are about to create our first one
2479 if not account or gajim.connections[account].connected < 2:
2480 ErrorDialog(_('You are not connected to the server'),
2481 _('Without a connection, you can not synchronise your contacts.'))
2482 raise GajimGeneralException, 'You are not connected to the server'
2483 self.account = account
2484 self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
2485 self.dialog = self.xml.get_object('synchronise_select_account_dialog')
2486 self.accounts_treeview = self.xml.get_object('accounts_treeview')
2487 model = gtk.ListStore(str, str, bool)
2488 self.accounts_treeview.set_model(model)
2489 # columns
2490 renderer = gtk.CellRendererText()
2491 self.accounts_treeview.insert_column_with_attributes(-1, _('Name'),
2492 renderer, text=0)
2493 renderer = gtk.CellRendererText()
2494 self.accounts_treeview.insert_column_with_attributes(-1, _('Server'),
2495 renderer, text=1)
2497 self.xml.connect_signals(self)
2498 self.init_accounts()
2499 self.dialog.show_all()
2501 def on_accounts_window_key_press_event(self, widget, event):
2502 if event.keyval == gtk.keysyms.Escape:
2503 self.window.destroy()
2505 def init_accounts(self):
2507 Initialize listStore with existing accounts
2509 model = self.accounts_treeview.get_model()
2510 model.clear()
2511 for remote_account in gajim.connections:
2512 if remote_account == self.account:
2513 # Do not show the account we're sync'ing
2514 continue
2515 iter_ = model.append()
2516 model.set(iter_, 0, remote_account, 1,
2517 gajim.get_hostname_from_account(remote_account))
2519 def on_cancel_button_clicked(self, widget):
2520 self.dialog.destroy()
2522 def on_ok_button_clicked(self, widget):
2523 sel = self.accounts_treeview.get_selection()
2524 (model, iter_) = sel.get_selected()
2525 if not iter_:
2526 return
2527 remote_account = model.get_value(iter_, 0).decode('utf-8')
2529 if gajim.connections[remote_account].connected < 2:
2530 ErrorDialog(_('This account is not connected to the server'),
2531 _('You cannot synchronize with an account unless it is connected.'))
2532 return
2533 else:
2534 try:
2535 SynchroniseSelectContactsDialog(self.account, remote_account)
2536 except GajimGeneralException:
2537 # if we showed ErrorDialog, there will not be dialog instance
2538 return
2539 self.dialog.destroy()
2541 class SynchroniseSelectContactsDialog:
2542 def __init__(self, account, remote_account):
2543 self.local_account = account
2544 self.remote_account = remote_account
2545 self.xml = gtkgui_helpers.get_gtk_builder(
2546 'synchronise_select_contacts_dialog.ui')
2547 self.dialog = self.xml.get_object('synchronise_select_contacts_dialog')
2548 self.contacts_treeview = self.xml.get_object('contacts_treeview')
2549 model = gtk.ListStore(bool, str)
2550 self.contacts_treeview.set_model(model)
2551 # columns
2552 renderer1 = gtk.CellRendererToggle()
2553 renderer1.set_property('activatable', True)
2554 renderer1.connect('toggled', self.toggled_callback)
2555 self.contacts_treeview.insert_column_with_attributes(-1,
2556 _('Synchronise'), renderer1, active=0)
2557 renderer2 = gtk.CellRendererText()
2558 self.contacts_treeview.insert_column_with_attributes(-1, _('Name'),
2559 renderer2, text=1)
2561 self.xml.connect_signals(self)
2562 self.init_contacts()
2563 self.dialog.show_all()
2565 def toggled_callback(self, cell, path):
2566 model = self.contacts_treeview.get_model()
2567 iter_ = model.get_iter(path)
2568 model[iter_][0] = not cell.get_active()
2570 def on_contacts_window_key_press_event(self, widget, event):
2571 if event.keyval == gtk.keysyms.Escape:
2572 self.window.destroy()
2574 def init_contacts(self):
2576 Initialize listStore with existing accounts
2578 model = self.contacts_treeview.get_model()
2579 model.clear()
2581 # recover local contacts
2582 local_jid_list = gajim.contacts.get_contacts_jid_list(self.local_account)
2584 remote_jid_list = gajim.contacts.get_contacts_jid_list(
2585 self.remote_account)
2586 for remote_jid in remote_jid_list:
2587 if remote_jid not in local_jid_list:
2588 iter_ = model.append()
2589 model.set(iter_, 0, True, 1, remote_jid)
2591 def on_cancel_button_clicked(self, widget):
2592 self.dialog.destroy()
2594 def on_ok_button_clicked(self, widget):
2595 model = self.contacts_treeview.get_model()
2596 iter_ = model.get_iter_root()
2597 while iter_:
2598 if model[iter_][0]:
2599 # it is selected
2600 remote_jid = model[iter_][1].decode('utf-8')
2601 message = 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
2602 gajim.get_hostname_from_account(self.remote_account)
2603 remote_contact = gajim.contacts.get_first_contact_from_jid(
2604 self.remote_account, remote_jid)
2605 # keep same groups and same nickname
2606 gajim.interface.roster.req_sub(self, remote_jid, message,
2607 self.local_account, groups = remote_contact.groups,
2608 nickname = remote_contact.name, auto_auth = True)
2609 iter_ = model.iter_next(iter_)
2610 self.dialog.destroy()
2612 class NewChatDialog(InputDialog):
2613 def __init__(self, account):
2614 self.account = account
2616 if len(gajim.connections) > 1:
2617 title = _('Start Chat with account %s') % account
2618 else:
2619 title = _('Start Chat')
2620 prompt_text = _('Fill in the nickname or the Jabber ID of the contact you would like\nto send a chat message to:')
2621 InputDialog.__init__(self, title, prompt_text, is_modal=False)
2623 self.completion_dict = {}
2624 liststore = gtkgui_helpers.get_completion_liststore(self.input_entry)
2625 self.completion_dict = helpers.get_contact_dict_for_account(account)
2626 # add all contacts to the model
2627 keys = sorted(self.completion_dict.keys())
2628 for jid in keys:
2629 contact = self.completion_dict[jid]
2630 img = gajim.interface.jabber_state_images['16'][contact.show]
2631 liststore.append((img.get_pixbuf(), jid))
2633 self.ok_handler = self.new_chat_response
2634 okbutton = self.xml.get_object('okbutton')
2635 okbutton.connect('clicked', self.on_okbutton_clicked)
2636 cancelbutton = self.xml.get_object('cancelbutton')
2637 cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
2638 self.dialog.set_transient_for(gajim.interface.roster.window)
2639 self.dialog.show_all()
2641 def new_chat_response(self, jid):
2643 Called when ok button is clicked
2645 if gajim.connections[self.account].connected <= 1:
2646 #if offline or connecting
2647 ErrorDialog(_('Connection not available'),
2648 _('Please make sure you are connected with "%s".') % self.account)
2649 return
2651 if jid in self.completion_dict:
2652 jid = self.completion_dict[jid].jid
2653 else:
2654 try:
2655 jid = helpers.parse_jid(jid)
2656 except helpers.InvalidFormat, e:
2657 ErrorDialog(_('Invalid JID'), e[0])
2658 return
2659 except:
2660 ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".') % jid)
2661 return
2662 gajim.interface.new_chat_from_jid(self.account, jid)
2664 class ChangePasswordDialog:
2665 def __init__(self, account, on_response):
2666 # 'account' can be None if we are about to create our first one
2667 if not account or gajim.connections[account].connected < 2:
2668 ErrorDialog(_('You are not connected to the server'),
2669 _('Without a connection, you can not change your password.'))
2670 raise GajimGeneralException, 'You are not connected to the server'
2671 self.account = account
2672 self.on_response = on_response
2673 self.xml = gtkgui_helpers.get_gtk_builder('change_password_dialog.ui')
2674 self.dialog = self.xml.get_object('change_password_dialog')
2675 self.password1_entry = self.xml.get_object('password1_entry')
2676 self.password2_entry = self.xml.get_object('password2_entry')
2677 self.dialog.connect('response', self.on_dialog_response)
2679 self.dialog.show_all()
2681 def on_dialog_response(self, dialog, response):
2682 if response != gtk.RESPONSE_OK:
2683 dialog.destroy()
2684 self.on_response(None)
2685 return
2686 password1 = self.password1_entry.get_text().decode('utf-8')
2687 if not password1:
2688 ErrorDialog(_('Invalid password'), _('You must enter a password.'))
2689 return
2690 password2 = self.password2_entry.get_text().decode('utf-8')
2691 if password1 != password2:
2692 ErrorDialog(_('Passwords do not match'),
2693 _('The passwords typed in both fields must be identical.'))
2694 return
2695 dialog.destroy()
2696 self.on_response(password1)
2698 class PopupNotificationWindow:
2699 def __init__(self, event_type, jid, account, msg_type='',
2700 path_to_image=None, title=None, text=None):
2701 self.account = account
2702 self.jid = jid
2703 self.msg_type = msg_type
2705 xml = gtkgui_helpers.get_gtk_builder('popup_notification_window.ui')
2706 self.window = xml.get_object('popup_notification_window')
2707 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLTIP)
2708 close_button = xml.get_object('close_button')
2709 event_type_label = xml.get_object('event_type_label')
2710 event_description_label = xml.get_object('event_description_label')
2711 eventbox = xml.get_object('eventbox')
2712 image = xml.get_object('notification_image')
2714 if not text:
2715 text = gajim.get_name_from_jid(account, jid) # default value of text
2716 if not title:
2717 title = ''
2719 event_type_label.set_markup(
2720 '<span foreground="black" weight="bold">%s</span>' %
2721 gobject.markup_escape_text(title))
2723 # set colors [ http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html ]
2724 self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
2726 # default image
2727 if not path_to_image:
2728 path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
2730 if event_type == _('Contact Signed In'):
2731 bg_color = 'limegreen'
2732 elif event_type == _('Contact Signed Out'):
2733 bg_color = 'red'
2734 elif event_type in (_('New Message'), _('New Single Message'),
2735 _('New Private Message'), _('New E-mail')):
2736 bg_color = 'dodgerblue'
2737 elif event_type == _('File Transfer Request'):
2738 bg_color = 'khaki'
2739 elif event_type == _('File Transfer Error'):
2740 bg_color = 'firebrick'
2741 elif event_type in (_('File Transfer Completed'),
2742 _('File Transfer Stopped')):
2743 bg_color = 'yellowgreen'
2744 elif event_type == _('Groupchat Invitation'):
2745 bg_color = 'tan1'
2746 elif event_type == _('Contact Changed Status'):
2747 bg_color = 'thistle2'
2748 else: # Unknown event! Shouldn't happen but deal with it
2749 bg_color = 'white'
2750 popup_bg_color = gtk.gdk.color_parse(bg_color)
2751 close_button.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
2752 eventbox.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
2753 event_description_label.set_markup('<span foreground="black">%s</span>' %
2754 gobject.markup_escape_text(text))
2756 # set the image
2757 image.set_from_file(path_to_image)
2759 # position the window to bottom-right of screen
2760 window_width, self.window_height = self.window.get_size()
2761 gajim.interface.roster.popups_notification_height += self.window_height
2762 pos_x = gajim.config.get('notification_position_x')
2763 if pos_x < 0:
2764 pos_x = gtk.gdk.screen_width() - window_width + pos_x + 1
2765 pos_y = gajim.config.get('notification_position_y')
2766 if pos_y < 0:
2767 pos_y = gtk.gdk.screen_height() - \
2768 gajim.interface.roster.popups_notification_height + pos_y + 1
2769 self.window.move(pos_x, pos_y)
2771 xml.connect_signals(self)
2772 self.window.show_all()
2773 timeout = gajim.config.get('notification_timeout')
2774 gobject.timeout_add_seconds(timeout, self.on_timeout)
2776 def on_close_button_clicked(self, widget):
2777 self.adjust_height_and_move_popup_notification_windows()
2779 def on_timeout(self):
2780 self.adjust_height_and_move_popup_notification_windows()
2782 def adjust_height_and_move_popup_notification_windows(self):
2783 #remove
2784 gajim.interface.roster.popups_notification_height -= self.window_height
2785 self.window.destroy()
2787 if len(gajim.interface.roster.popup_notification_windows) > 0:
2788 # we want to remove the first window added in the list
2789 gajim.interface.roster.popup_notification_windows.pop(0)
2791 # move the rest of popup windows
2792 gajim.interface.roster.popups_notification_height = 0
2793 for window_instance in gajim.interface.roster.popup_notification_windows:
2794 window_width, window_height = window_instance.window.get_size()
2795 gajim.interface.roster.popups_notification_height += window_height
2796 window_instance.window.move(gtk.gdk.screen_width() - window_width,
2797 gtk.gdk.screen_height() - \
2798 gajim.interface.roster.popups_notification_height)
2800 def on_popup_notification_window_button_press_event(self, widget, event):
2801 if event.button != 1:
2802 self.window.destroy()
2803 return
2804 gajim.interface.handle_event(self.account, self.jid, self.msg_type)
2805 self.adjust_height_and_move_popup_notification_windows()
2807 class SingleMessageWindow:
2809 SingleMessageWindow can send or show a received singled message depending on
2810 action argument which can be 'send' or 'receive'
2812 # Keep a reference on windows so garbage collector don't restroy them
2813 instances = []
2814 def __init__(self, account, to='', action='', from_whom='', subject='',
2815 message='', resource='', session=None, form_node=None):
2816 self.instances.append(self)
2817 self.account = account
2818 self.action = action
2820 self.subject = subject
2821 self.message = message
2822 self.to = to
2823 self.from_whom = from_whom
2824 self.resource = resource
2825 self.session = session
2827 self.xml = gtkgui_helpers.get_gtk_builder('single_message_window.ui')
2828 self.window = self.xml.get_object('single_message_window')
2829 self.count_chars_label = self.xml.get_object('count_chars_label')
2830 self.from_label = self.xml.get_object('from_label')
2831 self.from_entry = self.xml.get_object('from_entry')
2832 self.to_label = self.xml.get_object('to_label')
2833 self.to_entry = self.xml.get_object('to_entry')
2834 self.subject_entry = self.xml.get_object('subject_entry')
2835 self.message_scrolledwindow = self.xml.get_object(
2836 'message_scrolledwindow')
2837 self.message_textview = self.xml.get_object('message_textview')
2838 self.message_tv_buffer = self.message_textview.get_buffer()
2839 self.conversation_scrolledwindow = self.xml.get_object(
2840 'conversation_scrolledwindow')
2841 self.conversation_textview = conversation_textview.ConversationTextview(
2842 account)
2843 self.conversation_textview.tv.show()
2844 self.conversation_tv_buffer = self.conversation_textview.tv.get_buffer()
2845 self.xml.get_object('conversation_scrolledwindow').add(
2846 self.conversation_textview.tv)
2848 self.form_widget = None
2849 parent_box = self.xml.get_object('conversation_scrolledwindow').\
2850 get_parent()
2851 if form_node:
2852 dataform = dataforms.ExtendForm(node=form_node)
2853 self.form_widget = dataforms_widget.DataFormWidget(dataform)
2854 self.form_widget.show_all()
2855 parent_box.add(self.form_widget)
2856 parent_box.child_set_property(self.form_widget, 'position',
2857 parent_box.child_get_property(self.xml.get_object(
2858 'conversation_scrolledwindow'), 'position'))
2859 self.action = 'form'
2861 self.send_button = self.xml.get_object('send_button')
2862 self.reply_button = self.xml.get_object('reply_button')
2863 self.send_and_close_button = self.xml.get_object('send_and_close_button')
2864 self.cancel_button = self.xml.get_object('cancel_button')
2865 self.close_button = self.xml.get_object('close_button')
2866 self.message_tv_buffer.connect('changed', self.update_char_counter)
2867 if isinstance(to, list):
2868 jid = ', '.join( [i[0].get_full_jid() for i in to])
2869 self.to_entry.set_text(jid)
2870 self.to_entry.set_sensitive(False)
2871 else:
2872 self.to_entry.set_text(to)
2874 if gajim.config.get('use_speller') and HAS_GTK_SPELL and action == 'send':
2875 try:
2876 lang = gajim.config.get('speller_language')
2877 if not lang:
2878 lang = gajim.LANG
2879 gtkspell.Spell(self.conversation_textview.tv, lang)
2880 gtkspell.Spell(self.message_textview, lang)
2881 except (gobject.GError, TypeError, RuntimeError, OSError):
2882 AspellDictError(lang)
2884 self.prepare_widgets_for(self.action)
2886 # set_text(None) raises TypeError exception
2887 if self.subject is None:
2888 self.subject = ''
2889 self.subject_entry.set_text(self.subject)
2892 if to == '':
2893 liststore = gtkgui_helpers.get_completion_liststore(self.to_entry)
2894 self.completion_dict = helpers.get_contact_dict_for_account(account)
2895 keys = sorted(self.completion_dict.keys())
2896 for jid in keys:
2897 contact = self.completion_dict[jid]
2898 img = gajim.interface.jabber_state_images['16'][contact.show]
2899 liststore.append((img.get_pixbuf(), jid))
2900 else:
2901 self.completion_dict = {}
2902 self.xml.connect_signals(self)
2904 # get window position and size from config
2905 gtkgui_helpers.resize_window(self.window,
2906 gajim.config.get('single-msg-width'),
2907 gajim.config.get('single-msg-height'))
2908 gtkgui_helpers.move_window(self.window,
2909 gajim.config.get('single-msg-x-position'),
2910 gajim.config.get('single-msg-y-position'))
2912 self.window.show_all()
2914 def on_single_message_window_destroy(self, widget):
2915 self.instances.remove(self)
2916 c = gajim.contacts.get_contact_with_highest_priority(self.account,
2917 self.from_whom)
2918 if not c:
2919 # Groupchat is maybe already destroyed
2920 return
2921 if c.is_groupchat() and not self.from_whom in \
2922 gajim.interface.minimized_controls[self.account] and self.action == \
2923 'receive' and gajim.events.get_nb_roster_events(self.account,
2924 self.from_whom, types=['chat', 'normal']) == 0:
2925 gajim.interface.roster.remove_groupchat(self.from_whom, self.account)
2927 def set_cursor_to_end(self):
2928 end_iter = self.message_tv_buffer.get_end_iter()
2929 self.message_tv_buffer.place_cursor(end_iter)
2931 def save_pos(self):
2932 # save the window size and position
2933 x, y = self.window.get_position()
2934 gajim.config.set('single-msg-x-position', x)
2935 gajim.config.set('single-msg-y-position', y)
2936 width, height = self.window.get_size()
2937 gajim.config.set('single-msg-width', width)
2938 gajim.config.set('single-msg-height', height)
2939 gajim.interface.save_config()
2941 def on_single_message_window_delete_event(self, window, ev):
2942 self.save_pos()
2944 def prepare_widgets_for(self, action):
2945 if len(gajim.connections) > 1:
2946 if action == 'send':
2947 title = _('Single Message using account %s') % self.account
2948 else:
2949 title = _('Single Message in account %s') % self.account
2950 else:
2951 title = _('Single Message')
2953 if action == 'send': # prepare UI for Sending
2954 title = _('Send %s') % title
2955 self.send_button.show()
2956 self.send_and_close_button.show()
2957 self.to_label.show()
2958 self.to_entry.show()
2959 self.reply_button.hide()
2960 self.from_label.hide()
2961 self.from_entry.hide()
2962 self.conversation_scrolledwindow.hide()
2963 self.message_scrolledwindow.show()
2965 if self.message: # we come from a reply?
2966 self.message_textview.grab_focus()
2967 self.cancel_button.hide()
2968 self.close_button.show()
2969 self.message_tv_buffer.set_text(self.message)
2970 gobject.idle_add(self.set_cursor_to_end)
2971 else: # we write a new message (not from reply)
2972 self.close_button.hide()
2973 if self.to: # do we already have jid?
2974 self.subject_entry.grab_focus()
2976 elif action == 'receive': # prepare UI for Receiving
2977 title = _('Received %s') % title
2978 self.reply_button.show()
2979 self.from_label.show()
2980 self.from_entry.show()
2981 self.send_button.hide()
2982 self.send_and_close_button.hide()
2983 self.to_label.hide()
2984 self.to_entry.hide()
2985 self.conversation_scrolledwindow.show()
2986 self.message_scrolledwindow.hide()
2988 if self.message:
2989 self.conversation_textview.print_real_text(self.message)
2990 fjid = self.from_whom
2991 if self.resource:
2992 fjid += '/' + self.resource # Full jid of sender (with resource)
2993 self.from_entry.set_text(fjid)
2994 self.from_entry.set_property('editable', False)
2995 self.subject_entry.set_property('editable', False)
2996 self.reply_button.grab_focus()
2997 self.cancel_button.hide()
2998 self.close_button.show()
2999 elif action == 'form': # prepare UI for Receiving
3000 title = _('Form %s') % title
3001 self.send_button.show()
3002 self.send_and_close_button.show()
3003 self.to_label.show()
3004 self.to_entry.show()
3005 self.reply_button.hide()
3006 self.from_label.hide()
3007 self.from_entry.hide()
3008 self.conversation_scrolledwindow.hide()
3009 self.message_scrolledwindow.hide()
3011 self.window.set_title(title)
3013 def on_cancel_button_clicked(self, widget):
3014 self.save_pos()
3015 self.window.destroy()
3017 def on_close_button_clicked(self, widget):
3018 self.save_pos()
3019 self.window.destroy()
3021 def update_char_counter(self, widget):
3022 characters_no = self.message_tv_buffer.get_char_count()
3023 self.count_chars_label.set_text(unicode(characters_no))
3025 def send_single_message(self):
3026 if gajim.connections[self.account].connected <= 1:
3027 # if offline or connecting
3028 ErrorDialog(_('Connection not available'),
3029 _('Please make sure you are connected with "%s".') % self.account)
3030 return
3031 if isinstance(self.to, list):
3032 sender_list = [i[0].jid + '/' + i[0].resource for i in self.to]
3033 else:
3034 sender_list = [self.to_entry.get_text().decode('utf-8')]
3036 for to_whom_jid in sender_list:
3037 if to_whom_jid in self.completion_dict:
3038 to_whom_jid = self.completion_dict[to_whom_jid].jid
3039 try:
3040 to_whom_jid = helpers.parse_jid(to_whom_jid)
3041 except helpers.InvalidFormat:
3042 ErrorDialog(_('Invalid Jabber ID'),
3043 _('It is not possible to send a message to %s, this JID is not '
3044 'valid.') % to_whom_jid)
3045 return
3047 subject = self.subject_entry.get_text().decode('utf-8')
3048 begin, end = self.message_tv_buffer.get_bounds()
3049 message = self.message_tv_buffer.get_text(begin, end).decode('utf-8')
3051 if '/announce/' in to_whom_jid:
3052 gajim.connections[self.account].send_motd(to_whom_jid, subject,
3053 message)
3054 continue
3056 if self.session:
3057 session = self.session
3058 else:
3059 session = gajim.connections[self.account].make_new_session(
3060 to_whom_jid)
3062 if self.form_widget:
3063 form_node = self.form_widget.data_form
3064 else:
3065 form_node = None
3066 # FIXME: allow GPG message some day
3067 gajim.connections[self.account].send_message(to_whom_jid, message,
3068 keyID=None, type_='normal', subject=subject, session=session,
3069 form_node=form_node)
3071 self.subject_entry.set_text('') # we sent ok, clear the subject
3072 self.message_tv_buffer.set_text('') # we sent ok, clear the textview
3074 def on_send_button_clicked(self, widget):
3075 self.send_single_message()
3077 def on_reply_button_clicked(self, widget):
3078 # we create a new blank window to send and we preset RE: and to jid
3079 self.subject = _('RE: %s') % self.subject
3080 self.message = _('%s wrote:\n') % self.from_whom + self.message
3081 # add > at the begining of each line
3082 self.message = self.message.replace('\n', '\n> ') + '\n\n'
3083 self.window.destroy()
3084 SingleMessageWindow(self.account, to=self.from_whom, action='send',
3085 from_whom=self.from_whom, subject=self.subject, message=self.message,
3086 session=self.session)
3088 def on_send_and_close_button_clicked(self, widget):
3089 self.send_single_message()
3090 self.save_pos()
3091 self.window.destroy()
3093 def on_single_message_window_key_press_event(self, widget, event):
3094 if event.keyval == gtk.keysyms.Escape: # ESCAPE
3095 self.save_pos()
3096 self.window.destroy()
3098 class XMLConsoleWindow:
3099 def __init__(self, account):
3100 self.account = account
3102 self.xml = gtkgui_helpers.get_gtk_builder('xml_console_window.ui')
3103 self.window = self.xml.get_object('xml_console_window')
3104 self.input_textview = self.xml.get_object('input_textview')
3105 self.stanzas_log_textview = self.xml.get_object('stanzas_log_textview')
3106 self.input_tv_buffer = self.input_textview.get_buffer()
3107 buffer_ = self.stanzas_log_textview.get_buffer()
3108 end_iter = buffer_.get_end_iter()
3109 buffer_.create_mark('end', end_iter, False)
3111 self.tagIn = buffer_.create_tag('incoming')
3112 color = gajim.config.get('inmsgcolor')
3113 self.tagIn.set_property('foreground', color)
3114 self.tagInPresence = buffer_.create_tag('incoming_presence')
3115 self.tagInPresence.set_property('foreground', color)
3116 self.tagInMessage = buffer_.create_tag('incoming_message')
3117 self.tagInMessage.set_property('foreground', color)
3118 self.tagInIq = buffer_.create_tag('incoming_iq')
3119 self.tagInIq.set_property('foreground', color)
3121 self.tagOut = buffer_.create_tag('outgoing')
3122 color = gajim.config.get('outmsgcolor')
3123 self.tagOut.set_property('foreground', color)
3124 self.tagOutPresence = buffer_.create_tag('outgoing_presence')
3125 self.tagOutPresence.set_property('foreground', color)
3126 self.tagOutMessage = buffer_.create_tag('outgoing_message')
3127 self.tagOutMessage.set_property('foreground', color)
3128 self.tagOutIq = buffer_.create_tag('outgoing_iq')
3129 self.tagOutIq.set_property('foreground', color)
3130 buffer_.create_tag('') # Default tag
3132 self.enabled = True
3133 self.xml.get_object('enable_checkbutton').set_active(True)
3135 self.input_textview.modify_text(
3136 gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
3138 if len(gajim.connections) > 1:
3139 title = _('XML Console for %s') % self.account
3140 else:
3141 title = _('XML Console')
3143 self.window.set_title(title)
3144 self.window.show_all()
3145 gajim.ged.register_event_handler('stanza-received', ged.GUI1,
3146 self._nec_stanza_received)
3147 gajim.ged.register_event_handler('stanza-sent', ged.GUI1,
3148 self._nec_stanza_sent)
3150 self.xml.connect_signals(self)
3152 def on_xml_console_window_destroy(self, widget):
3153 del gajim.interface.instances[self.account]['xml_console']
3154 gajim.ged.remove_event_handler('stanza-received', ged.GUI1,
3155 self._nec_stanza_received)
3156 gajim.ged.remove_event_handler('stanza-sent', ged.GUI1,
3157 self._nec_stanza_sent)
3159 def on_clear_button_clicked(self, widget):
3160 buffer_ = self.stanzas_log_textview.get_buffer()
3161 buffer_.set_text('')
3163 def on_enable_checkbutton_toggled(self, widget):
3164 self.enabled = widget.get_active()
3166 def on_in_stanza_checkbutton_toggled(self, widget):
3167 active = widget.get_active()
3168 self.tagIn.set_property('invisible', active)
3169 self.tagInPresence.set_property('invisible', active)
3170 self.tagInMessage.set_property('invisible', active)
3171 self.tagInIq.set_property('invisible', active)
3173 def on_presence_stanza_checkbutton_toggled(self, widget):
3174 active = widget.get_active()
3175 self.tagInPresence.set_property('invisible', active)
3176 self.tagOutPresence.set_property('invisible', active)
3178 def on_out_stanza_checkbutton_toggled(self, widget):
3179 active = widget.get_active()
3180 self.tagOut.set_property('invisible', active)
3181 self.tagOutPresence.set_property('invisible', active)
3182 self.tagOutMessage.set_property('invisible', active)
3183 self.tagOutIq.set_property('invisible', active)
3185 def on_message_stanza_checkbutton_toggled(self, widget):
3186 active = widget.get_active()
3187 self.tagInMessage.set_property('invisible', active)
3188 self.tagOutMessage.set_property('invisible', active)
3190 def on_iq_stanza_checkbutton_toggled(self, widget):
3191 active = widget.get_active()
3192 self.tagInIq.set_property('invisible', active)
3193 self.tagOutIq.set_property('invisible', active)
3195 def scroll_to_end(self, ):
3196 parent = self.stanzas_log_textview.get_parent()
3197 buffer_ = self.stanzas_log_textview.get_buffer()
3198 end_mark = buffer_.get_mark('end')
3199 if not end_mark:
3200 return False
3201 self.stanzas_log_textview.scroll_to_mark(end_mark, 0, True, 0, 1)
3202 adjustment = parent.get_hadjustment()
3203 adjustment.set_value(0)
3204 return False
3206 def print_stanza(self, stanza, kind):
3207 # kind must be 'incoming' or 'outgoing'
3208 if not self.enabled:
3209 return
3210 if not stanza:
3211 return
3213 buffer = self.stanzas_log_textview.get_buffer()
3214 at_the_end = False
3215 end_iter = buffer.get_end_iter()
3216 end_rect = self.stanzas_log_textview.get_iter_location(end_iter)
3217 visible_rect = self.stanzas_log_textview.get_visible_rect()
3218 if end_rect.y <= (visible_rect.y + visible_rect.height):
3219 at_the_end = True
3220 end_iter = buffer.get_end_iter()
3222 type_ = ''
3223 if stanza[1:9] == 'presence':
3224 type_ = 'presence'
3225 elif stanza[1:8] == 'message':
3226 type_ = 'message'
3227 elif stanza[1:3] == 'iq':
3228 type_ = 'iq'
3230 if type_:
3231 type_ = kind + '_' + type_
3232 else:
3233 type_ = kind # 'incoming' or 'outgoing'
3235 if kind == 'incoming':
3236 buffer.insert_with_tags_by_name(end_iter, '<!-- In -->\n', type_)
3237 elif kind == 'outgoing':
3238 buffer.insert_with_tags_by_name(end_iter, '<!-- Out -->\n', type_)
3239 end_iter = buffer.get_end_iter()
3240 buffer.insert_with_tags_by_name(end_iter, stanza.replace('><', '>\n<') \
3241 + '\n\n', type_)
3242 if at_the_end:
3243 gobject.idle_add(self.scroll_to_end)
3245 def _nec_stanza_received(self, obj):
3246 if obj.conn.name != self.account:
3247 return
3248 self.print_stanza(obj.stanza_str, 'incoming')
3250 def _nec_stanza_sent(self, obj):
3251 if obj.conn.name != self.account:
3252 return
3253 self.print_stanza(obj.stanza_str, 'outgoing')
3255 def on_send_button_clicked(self, widget):
3256 if gajim.connections[self.account].connected <= 1:
3257 # if offline or connecting
3258 ErrorDialog(_('Connection not available'),
3259 _('Please make sure you are connected with "%s".') % \
3260 self.account)
3261 return
3262 begin_iter, end_iter = self.input_tv_buffer.get_bounds()
3263 stanza = self.input_tv_buffer.get_text(begin_iter, end_iter).decode(
3264 'utf-8')
3265 if stanza:
3266 gajim.connections[self.account].send_stanza(stanza)
3267 self.input_tv_buffer.set_text('') # we sent ok, clear the textview
3269 def on_presence_button_clicked(self, widget):
3270 self.input_tv_buffer.set_text(
3271 '<presence><show></show><status></status><priority></priority>'
3272 '</presence>')
3274 def on_iq_button_clicked(self, widget):
3275 self.input_tv_buffer.set_text(
3276 '<iq to="" type=""><query xmlns=""></query></iq>')
3278 def on_message_button_clicked(self, widget):
3279 self.input_tv_buffer.set_text(
3280 '<message to="" type=""><body></body></message>')
3282 def on_expander_activate(self, widget):
3283 if not widget.get_expanded(): # it's the opposite!
3284 # it's expanded!!
3285 self.input_textview.grab_focus()
3287 #Action that can be done with an incoming list of contacts
3288 TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
3289 'remove': _('remove')}
3290 class RosterItemExchangeWindow:
3292 Windows used when someone send you a exchange contact suggestion
3295 def __init__(self, account, action, exchange_list, jid_from,
3296 message_body=None):
3297 self.account = account
3298 self.action = action
3299 self.exchange_list = exchange_list
3300 self.message_body = message_body
3301 self.jid_from = jid_from
3303 show_dialog = False
3305 # Connect to gtk builder
3306 self.xml = gtkgui_helpers.get_gtk_builder(
3307 'roster_item_exchange_window.ui')
3308 self.window = self.xml.get_object('roster_item_exchange_window')
3310 # Add Widgets.
3311 for widget_to_add in ['accept_button_label', 'type_label',
3312 'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
3313 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3315 # Set labels
3316 # self.action can be 'add', 'modify' or 'remove'
3317 self.type_label.set_label(
3318 _('<b>%(jid)s</b> would like you to <b>%(action)s</b> some contacts '
3319 'in your roster.') % {'jid': jid_from,
3320 'action': TRANSLATED_ACTION[self.action]})
3321 if message_body:
3322 buffer_ = self.body_textview.get_buffer()
3323 buffer_.set_text(self.message_body)
3324 else:
3325 self.body_scrolledwindow.hide()
3326 # Treeview
3327 model = gtk.ListStore(bool, str, str, str, str)
3328 self.items_list_treeview.set_model(model)
3329 # columns
3330 renderer1 = gtk.CellRendererToggle()
3331 renderer1.set_property('activatable', True)
3332 renderer1.connect('toggled', self.toggled_callback)
3333 if self.action == 'add':
3334 title = _('Add')
3335 elif self.action == 'modify':
3336 title = _('Modify')
3337 elif self.action == 'delete':
3338 title = _('Delete')
3339 self.items_list_treeview.insert_column_with_attributes(-1, title,
3340 renderer1, active=0)
3341 renderer2 = gtk.CellRendererText()
3342 self.items_list_treeview.insert_column_with_attributes(-1, _('Jabber ID'),
3343 renderer2, text=1)
3344 renderer3 = gtk.CellRendererText()
3345 self.items_list_treeview.insert_column_with_attributes(-1, _('Name'),
3346 renderer3, text=2)
3347 renderer4 = gtk.CellRendererText()
3348 self.items_list_treeview.insert_column_with_attributes(-1, _('Groups'),
3349 renderer4, text=3)
3351 # Init contacts
3352 model = self.items_list_treeview.get_model()
3353 model.clear()
3355 if action == 'add':
3356 for jid in self.exchange_list:
3357 groups = ''
3358 is_in_roster = True
3359 contact = gajim.contacts.get_contact_with_highest_priority(
3360 self.account, jid)
3361 if not contact:
3362 is_in_roster = False
3363 name = self.exchange_list[jid][0]
3364 num_list = len(self.exchange_list[jid][1])
3365 current = 0
3366 for group in self.exchange_list[jid][1]:
3367 current += 1
3368 if contact and not group in contact.groups:
3369 is_in_roster = False
3370 if current == num_list:
3371 groups = groups + group
3372 else:
3373 groups = groups + group + ', '
3374 if not is_in_roster:
3375 show_dialog = True
3376 iter_ = model.append()
3377 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3379 # Change label for accept_button to action name instead of 'OK'.
3380 self.accept_button_label.set_label(_('Add'))
3381 elif action == 'modify':
3382 for jid in self.exchange_list:
3383 groups = ''
3384 is_in_roster = True
3385 is_right = True
3386 contact = gajim.contacts.get_contact_with_highest_priority(
3387 self.account, jid)
3388 name = self.exchange_list[jid][0]
3389 if not contact:
3390 is_in_roster = False
3391 is_right = False
3392 else:
3393 if name != contact.name:
3394 is_right = False
3395 num_list = len(self.exchange_list[jid][1])
3396 current = 0
3397 for group in self.exchange_list[jid][1]:
3398 current += 1
3399 if contact and not group in contact.groups:
3400 is_right = False
3401 if current == num_list:
3402 groups = groups + group
3403 else:
3404 groups = groups + group + ', '
3405 if not is_right and is_in_roster:
3406 show_dialog = True
3407 iter_ = model.append()
3408 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3410 # Change label for accept_button to action name instead of 'OK'.
3411 self.accept_button_label.set_label(_('Modify'))
3412 elif action == 'delete':
3413 for jid in self.exchange_list:
3414 groups = ''
3415 is_in_roster = True
3416 contact = gajim.contacts.get_contact_with_highest_priority(
3417 self.account, jid)
3418 name = self.exchange_list[jid][0]
3419 if not contact:
3420 is_in_roster = False
3421 num_list = len(self.exchange_list[jid][1])
3422 current = 0
3423 for group in self.exchange_list[jid][1]:
3424 current += 1
3425 if current == num_list:
3426 groups = groups + group
3427 else:
3428 groups = groups + group + ', '
3429 if is_in_roster:
3430 show_dialog = True
3431 iter_ = model.append()
3432 model.set(iter_, 0, True, 1, jid, 2, name, 3, groups)
3434 # Change label for accept_button to action name instead of 'OK'.
3435 self.accept_button_label.set_label(_('Delete'))
3437 if show_dialog:
3438 self.window.show_all()
3439 self.xml.connect_signals(self)
3441 def toggled_callback(self, cell, path):
3442 model = self.items_list_treeview.get_model()
3443 iter_ = model.get_iter(path)
3444 model[iter_][0] = not cell.get_active()
3446 def on_accept_button_clicked(self, widget):
3447 model = self.items_list_treeview.get_model()
3448 iter_ = model.get_iter_root()
3449 if self.action == 'add':
3450 a = 0
3451 while iter_:
3452 if model[iter_][0]:
3453 a+=1
3454 # it is selected
3455 #remote_jid = model[iter_][1].decode('utf-8')
3456 message = _('%s suggested me to add you in my roster.'
3457 % self.jid_from)
3458 # keep same groups and same nickname
3459 groups = model[iter_][3].split(', ')
3460 if groups == ['']:
3461 groups = []
3462 jid = model[iter_][1].decode('utf-8')
3463 if gajim.jid_is_transport(self.jid_from):
3464 gajim.connections[self.account].automatically_added.append(
3465 jid)
3466 gajim.interface.roster.req_sub(self, jid, message,
3467 self.account, groups=groups, nickname=model[iter_][2],
3468 auto_auth=True)
3469 iter_ = model.iter_next(iter_)
3470 InformationDialog(_('Added %s contacts') % str(a))
3471 elif self.action == 'modify':
3472 a = 0
3473 while iter_:
3474 if model[iter_][0]:
3475 a+=1
3476 # it is selected
3477 jid = model[iter_][1].decode('utf-8')
3478 # keep same groups and same nickname
3479 groups = model[iter_][3].split(', ')
3480 if groups == ['']:
3481 groups = []
3482 for u in gajim.contacts.get_contact(self.account, jid):
3483 u.name = model[iter_][2]
3484 gajim.connections[self.account].update_contact(jid,
3485 model[iter_][2], groups)
3486 self.draw_contact(jid, self.account)
3487 # Update opened chat
3488 ctrl = gajim.interface.msg_win_mgr.get_control(jid, self.account)
3489 if ctrl:
3490 ctrl.update_ui()
3491 win = gajim.interface.msg_win_mgr.get_window(jid,
3492 self.account)
3493 win.redraw_tab(ctrl)
3494 win.show_title()
3495 iter_ = model.iter_next(iter_)
3496 elif self.action == 'delete':
3497 a = 0
3498 while iter_:
3499 if model[iter_][0]:
3500 a+=1
3501 # it is selected
3502 jid = model[iter_][1].decode('utf-8')
3503 gajim.connections[self.account].unsubscribe(jid)
3504 gajim.interface.roster.remove_contact(jid, self.account)
3505 gajim.contacts.remove_jid(self.account, jid)
3506 iter_ = model.iter_next(iter_)
3507 InformationDialog(_('Removed %s contacts') % str(a))
3508 self.window.destroy()
3510 def on_cancel_button_clicked(self, widget):
3511 self.window.destroy()
3514 class ItemArchivingPreferencesWindow:
3515 otr_name = ('approve', 'concede', 'forbid', 'oppose', 'prefer', 'require')
3516 otr_index = dict([(j, i) for i, j in enumerate(otr_name)])
3517 save_name = ('body', 'false', 'message', 'stream')
3518 save_index = dict([(j, i) for i, j in enumerate(save_name)])
3520 def __init__(self, account, item):
3521 self.account = account
3522 self.item = item
3523 if self.item and self.item != 'Default':
3524 self.item_config = gajim.connections[self.account].items[self.item]
3525 else:
3526 self.item_config = gajim.connections[self.account].default
3527 self.waiting = None
3529 # Connect to gtk builder
3530 self.xml = gtkgui_helpers.get_gtk_builder(
3531 'item_archiving_preferences_window.ui')
3532 self.window = self.xml.get_object('item_archiving_preferences_window')
3534 # Add Widgets
3535 for widget_to_add in ('jid_entry', 'expire_entry', 'otr_combobox',
3536 'save_combobox', 'cancel_button', 'ok_button', 'progressbar'):
3537 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3539 if self.item:
3540 self.jid_entry.set_text(self.item)
3541 expire_value = self.item_config['expire'] or ''
3542 self.otr_combobox.set_active(self.otr_index[self.item_config['otr']])
3543 self.save_combobox.set_active(
3544 self.save_index[self.item_config['save']])
3545 self.expire_entry.set_text(expire_value)
3547 self.window.set_title(_('Archiving Preferences for %s') % self.account)
3549 self.window.show_all()
3550 self.progressbar.hide()
3551 self.xml.connect_signals(self)
3553 def update_progressbar(self):
3554 if self.waiting:
3555 self.progressbar.pulse()
3556 return True
3557 return False
3559 def on_otr_combobox_changed(self, widget):
3560 otr = self.otr_name[self.otr_combobox.get_active()]
3561 if otr == 'require':
3562 self.save_combobox.set_active(self.save_index['false'])
3564 def on_ok_button_clicked(self, widget):
3565 # Return directly if operation in progress
3566 if self.waiting:
3567 return
3569 item = self.jid_entry.get_text()
3570 otr = self.otr_name[self.otr_combobox.get_active()]
3571 save = self.save_name[self.save_combobox.get_active()]
3572 expire = self.expire_entry.get_text()
3574 if self.item != 'Default':
3575 try:
3576 item = helpers.parse_jid(item)
3577 except helpers.InvalidFormat, s:
3578 pritext = _('Invalid User ID')
3579 ErrorDialog(pritext, str(s))
3580 return
3582 if expire:
3583 try:
3584 if int(expire) < 0 or str(int(expire)) != expire:
3585 raise ValueError
3586 except ValueError:
3587 pritext = _('Invalid expire value')
3588 sectext = _('Expire must be a valid positive integer.')
3589 ErrorDialog(pritext, sectext)
3590 return
3592 if not (item == self.item and expire == self.item_config['expire'] and
3593 otr == self.item_config['otr'] and save == self.item_config['save']):
3594 if not self.item or self.item == item:
3595 if self.item == 'Default':
3596 self.waiting = 'default'
3597 gajim.connections[self.account].set_default(
3598 otr, save, expire)
3599 else:
3600 self.waiting = 'item'
3601 gajim.connections[self.account].append_or_update_item(
3602 item, otr, save, expire)
3603 else:
3604 self.waiting = 'item'
3605 gajim.connections[self.account].append_or_update_item(
3606 item, otr, save, expire)
3607 gajim.connections[self.account].remove_item(self.item)
3608 self.launch_progressbar()
3609 #self.window.destroy()
3611 def on_cancel_button_clicked(self, widget):
3612 self.window.destroy()
3614 def on_item_archiving_preferences_window_destroy(self, widget):
3615 if self.item:
3616 key_name = 'edit_item_archiving_preferences_%s' % self.item
3617 else:
3618 key_name = 'new_item_archiving_preferences'
3619 if key_name in gajim.interface.instances[self.account]:
3620 del gajim.interface.instances[self.account][key_name]
3622 def launch_progressbar(self):
3623 self.progressbar.show()
3624 self.update_progressbar_timeout_id = gobject.timeout_add(
3625 100, self.update_progressbar)
3627 def response_arrived(self, data):
3628 if self.waiting:
3629 self.window.destroy()
3631 def error_arrived(self, error):
3632 if self.waiting:
3633 self.waiting = None
3634 self.progressbar.hide()
3635 pritext = _('There is an error with the form')
3636 sectext = error
3637 ErrorDialog(pritext, sectext)
3640 class ArchivingPreferencesWindow:
3641 auto_name = ('false', 'true')
3642 auto_index = dict([(j, i) for i, j in enumerate(auto_name)])
3643 method_foo_name = ('prefer', 'concede', 'forbid')
3644 method_foo_index = dict([(j, i) for i, j in enumerate(method_foo_name)])
3646 def __init__(self, account):
3647 self.account = account
3648 self.waiting = []
3650 # Connect to glade
3651 self.xml = gtkgui_helpers.get_gtk_builder(
3652 'archiving_preferences_window.ui')
3653 self.window = self.xml.get_object('archiving_preferences_window')
3655 # Add Widgets
3656 for widget_to_add in ('auto_combobox', 'method_auto_combobox',
3657 'method_local_combobox', 'method_manual_combobox', 'close_button',
3658 'item_treeview', 'item_notebook', 'otr_combobox', 'save_combobox',
3659 'expire_entry', 'remove_button', 'edit_button'):
3660 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3662 self.auto_combobox.set_active(
3663 self.auto_index[gajim.connections[self.account].auto])
3664 self.method_auto_combobox.set_active(
3665 self.method_foo_index[gajim.connections[self.account].method_auto])
3666 self.method_local_combobox.set_active(
3667 self.method_foo_index[gajim.connections[self.account].method_local])
3668 self.method_manual_combobox.set_active(
3669 self.method_foo_index[gajim.connections[self.account].\
3670 method_manual])
3672 model = gtk.ListStore(str, str, str, str)
3673 self.item_treeview.set_model(model)
3674 col = gtk.TreeViewColumn('jid')
3675 self.item_treeview.append_column(col)
3676 renderer = gtk.CellRendererText()
3677 col.pack_start(renderer, True)
3678 col.set_attributes(renderer, text=0)
3680 col = gtk.TreeViewColumn('expire')
3681 col.pack_start(renderer, True)
3682 col.set_attributes(renderer, text=1)
3683 self.item_treeview.append_column(col)
3685 col = gtk.TreeViewColumn('otr')
3686 col.pack_start(renderer, True)
3687 col.set_attributes(renderer, text=2)
3688 self.item_treeview.append_column(col)
3690 col = gtk.TreeViewColumn('save')
3691 col.pack_start(renderer, True)
3692 col.set_attributes(renderer, text=3)
3693 self.item_treeview.append_column(col)
3695 self.fill_items()
3697 self.current_item = None
3699 def sort_items(model, iter1, iter2):
3700 item1 = model.get_value(iter1, 0)
3701 item2 = model.get_value(iter2, 0)
3702 if item1 == 'Default':
3703 return -1
3704 if item2 == 'Default':
3705 return 1
3706 if '@' in item1:
3707 if '@' not in item2:
3708 return 1
3709 elif '@' in item2:
3710 return -1
3711 if item1 < item2:
3712 return -1
3713 if item1 > item2:
3714 return 1
3715 # item1 == item2 ? WTF?
3716 return 0
3718 model.set_sort_column_id(0, gtk.SORT_ASCENDING)
3719 model.set_sort_func(0, sort_items)
3721 self.remove_button.set_sensitive(False)
3722 self.edit_button.set_sensitive(False)
3724 self.window.set_title(_('Archiving Preferences for %s') % self.account)
3726 gajim.ged.register_event_handler(
3727 'archiving-preferences-changed-received', ged.GUI1,
3728 self._nec_archiving_changed_received)
3729 gajim.ged.register_event_handler('archiving-error-received', ged.GUI1,
3730 self._nec_archiving_error)
3732 self.window.show_all()
3734 self.xml.connect_signals(self)
3736 def on_add_item_button_clicked(self, widget):
3737 key_name = 'new_item_archiving_preferences'
3738 if key_name in gajim.interface.instances[self.account]:
3739 gajim.interface.instances[self.account][key_name].window.present()
3740 else:
3741 gajim.interface.instances[self.account][key_name] = \
3742 ItemArchivingPreferencesWindow(self.account, '')
3744 def on_remove_item_button_clicked(self, widget):
3745 if not self.current_item:
3746 return
3748 self.waiting.append('itemremove')
3749 sel = self.item_treeview.get_selection()
3750 (model, iter_) = sel.get_selected()
3751 gajim.connections[self.account].remove_item(model[iter_][0])
3752 model.remove(iter_)
3753 self.remove_button.set_sensitive(False)
3754 self.edit_button.set_sensitive(False)
3756 def on_edit_item_button_clicked(self, widget):
3757 if not self.current_item:
3758 return
3760 key_name = 'edit_item_archiving_preferences_%s' % self.current_item
3761 if key_name in gajim.interface.instances[self.account]:
3762 gajim.interface.instances[self.account][key_name].window.present()
3763 else:
3764 gajim.interface.instances[self.account][key_name] = \
3765 ItemArchivingPreferencesWindow(self.account, self.current_item)
3767 def on_item_treeview_cursor_changed(self, widget):
3768 sel = self.item_treeview.get_selection()
3769 (model, iter_) = sel.get_selected()
3770 item = None
3771 if iter_:
3772 item = model[iter_][0]
3773 if self.current_item and self.current_item == item:
3774 return
3776 self.current_item = item
3777 if self.current_item == 'Default':
3778 self.remove_button.set_sensitive(False)
3779 self.edit_button.set_sensitive(True)
3780 elif self.current_item:
3781 self.remove_button.set_sensitive(True)
3782 self.edit_button.set_sensitive(True)
3783 else:
3784 self.remove_button.set_sensitive(False)
3785 self.edit_button.set_sensitive(False)
3787 def on_auto_combobox_changed(self, widget):
3788 save = self.auto_name[widget.get_active()]
3789 gajim.connections[self.account].set_auto(save)
3791 def on_method_foo_combobox_changed(self, widget):
3792 # We retrieve method type from widget name
3793 # ('foo' in 'method_foo_combobox')
3794 method_type = widget.name.split('_')[1]
3795 use = self.method_foo_name[widget.get_active()]
3796 self.waiting.append('method_%s' % method_type)
3797 gajim.connections[self.account].set_method(method_type, use)
3799 def get_child_window(self):
3800 edit_key_name = 'edit_item_archiving_preferences_%s' % self.current_item
3801 new_key_name = 'new_item_archiving_preferences'
3803 if edit_key_name in gajim.interface.instances[self.account]:
3804 return gajim.interface.instances[self.account][edit_key_name]
3806 if new_key_name in gajim.interface.instances[self.account]:
3807 return gajim.interface.instances[self.account][new_key_name]
3809 def _nec_archiving_changed_received(self, obj):
3810 if obj.conn.name != self.account:
3811 return
3812 for key in ('auto', 'method_auto', 'method_local', 'method_manual'):
3813 if key in obj.conf and key in self.waiting:
3814 self.waiting.remove(key)
3815 if 'default' in obj.conf:
3816 key_name = 'edit_item_archiving_preferences_%s' % \
3817 self.current_item
3818 if key_name in gajim.interface.instances[self.account]:
3819 gajim.interface.instances[self.account][key_name].\
3820 response_arrived(obj.conf['default'])
3821 self.fill_items(True)
3822 for jid, pref in obj.new_items.items():
3823 child = self.get_child_window()
3824 if child:
3825 is_new = not child.item
3826 child.response_arrived(pref)
3827 if is_new:
3828 model = self.item_treeview.get_model()
3829 model.append((jid, pref['expire'], pref['otr'],
3830 pref['save']))
3831 continue
3832 self.fill_items(True)
3833 if 'itemremove' in self.waiting and obj.removed_items:
3834 self.waiting.remove('itemremove')
3835 self.fill_items(True)
3837 def fill_items(self, clear=False):
3838 model = self.item_treeview.get_model()
3839 if clear:
3840 model.clear()
3841 default_config = gajim.connections[self.account].default
3842 expire_value = default_config['expire'] or ''
3843 model.append(('Default', expire_value,
3844 default_config['otr'], default_config['save']))
3845 for item, item_config in \
3846 gajim.connections[self.account].items.items():
3847 expire_value = item_config['expire'] or ''
3848 model.append((item, expire_value, item_config['otr'],
3849 item_config['save']))
3851 def _nec_archiving_error(self, obj):
3852 if obj.conn.name != self.account:
3853 return
3854 if self.waiting:
3855 pritext = _('There is an error')
3856 sectext = obj.error_msg
3857 ErrorDialog(pritext, sectext)
3858 self.waiting.pop()
3859 else:
3860 child = self.get_child_window()
3861 if child:
3862 child.error_arrived(obj.error_msg)
3864 def on_close_button_clicked(self, widget):
3865 self.window.destroy()
3867 def on_archiving_preferences_window_destroy(self, widget):
3868 gajim.ged.remove_event_handler(
3869 'archiving-preferences-changed-received', ged.GUI1,
3870 self._nec_archiving_changed_received)
3871 gajim.ged.remove_event_handler('archiving-error-received', ged.GUI1,
3872 self._nec_archiving_error)
3873 if 'archiving_preferences' in gajim.interface.instances[self.account]:
3874 del gajim.interface.instances[self.account]['archiving_preferences']
3877 class PrivacyListWindow:
3879 Window that is used for creating NEW or EDITING already there privacy lists
3882 def __init__(self, account, privacy_list_name, action):
3883 '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
3884 or edit an already existing one'''
3885 self.account = account
3886 self.privacy_list_name = privacy_list_name
3888 # Dicts and Default Values
3889 self.active_rule = ''
3890 self.global_rules = {}
3891 self.list_of_groups = {}
3893 self.max_order = 0
3895 # Default Edit Values
3896 self.edit_rule_type = 'jid'
3897 self.allow_deny = 'allow'
3899 # Connect to gtk builder
3900 self.xml = gtkgui_helpers.get_gtk_builder('privacy_list_window.ui')
3901 self.window = self.xml.get_object('privacy_list_edit_window')
3903 # Add Widgets
3905 for widget_to_add in ('title_hbox', 'privacy_lists_title_label',
3906 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
3907 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
3908 'list_of_rules_combobox', 'delete_open_buttons_hbox',
3909 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
3910 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
3911 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
3912 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
3913 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
3914 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
3915 'edit_view_status_checkbutton', 'edit_all_checkbutton',
3916 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
3917 'privacy_list_refresh_button', 'privacy_list_close_button',
3918 'edit_send_status_checkbutton', 'add_edit_vbox',
3919 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
3920 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
3922 self.privacy_lists_title_label.set_label(
3923 _('Privacy List <b><i>%s</i></b>') % \
3924 gobject.markup_escape_text(self.privacy_list_name))
3926 if len(gajim.connections) > 1:
3927 title = _('Privacy List for %s') % self.account
3928 else:
3929 title = _('Privacy List')
3931 self.delete_rule_button.set_sensitive(False)
3932 self.open_rule_button.set_sensitive(False)
3933 self.privacy_list_active_checkbutton.set_sensitive(False)
3934 self.privacy_list_default_checkbutton.set_sensitive(False)
3935 self.list_of_rules_combobox.set_sensitive(False)
3937 # set jabber id completion
3938 jids_list_store = gtk.ListStore(gobject.TYPE_STRING)
3939 for jid in gajim.contacts.get_jid_list(self.account):
3940 jids_list_store.append([jid])
3941 jid_entry_completion = gtk.EntryCompletion()
3942 jid_entry_completion.set_text_column(0)
3943 jid_entry_completion.set_model(jids_list_store)
3944 jid_entry_completion.set_popup_completion(True)
3945 self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
3946 if action == 'EDIT':
3947 self.refresh_rules()
3949 count = 0
3950 for group in gajim.groups[self.account]:
3951 self.list_of_groups[group] = count
3952 count += 1
3953 self.edit_type_group_combobox.append_text(group)
3954 self.edit_type_group_combobox.set_active(0)
3956 self.window.set_title(title)
3958 gajim.ged.register_event_handler('privacy-list-received', ged.GUI1,
3959 self._nec_privacy_list_received)
3960 gajim.ged.register_event_handler('privacy-list-active-default',
3961 ged.GUI1, self._nec_privacy_list_active_default)
3963 self.window.show_all()
3964 self.add_edit_vbox.hide()
3966 self.xml.connect_signals(self)
3968 def on_privacy_list_edit_window_destroy(self, widget):
3969 key_name = 'privacy_list_%s' % self.privacy_list_name
3970 if key_name in gajim.interface.instances[self.account]:
3971 del gajim.interface.instances[self.account][key_name]
3972 gajim.ged.remove_event_handler('privacy-list-received', ged.GUI1,
3973 self._nec_privacy_list_received)
3974 gajim.ged.remove_event_handler('privacy-list-active-default',
3975 ged.GUI1, self._nec_privacy_list_active_default)
3977 def _nec_privacy_list_active_default(self, obj):
3978 if obj.conn.name != self.account:
3979 return
3980 if obj.active_list == self.privacy_list_name:
3981 self.privacy_list_active_checkbutton.set_active(True)
3982 else:
3983 self.privacy_list_active_checkbutton.set_active(False)
3984 if obj.default_list == self.privacy_list_name:
3985 self.privacy_list_default_checkbutton.set_active(True)
3986 else:
3987 self.privacy_list_default_checkbutton.set_active(False)
3989 def privacy_list_received(self, rules):
3990 self.list_of_rules_combobox.get_model().clear()
3991 self.global_rules = {}
3992 for rule in rules:
3993 if 'type' in rule:
3994 text_item = _('Order: %(order)s, action: %(action)s, type: %(type)s'
3995 ', value: %(value)s') % {'order': rule['order'],
3996 'action': rule['action'], 'type': rule['type'],
3997 'value': rule['value']}
3998 else:
3999 text_item = _('Order: %(order)s, action: %(action)s') % \
4000 {'order': rule['order'], 'action': rule['action']}
4001 if int(rule['order']) > self.max_order:
4002 self.max_order = int(rule['order'])
4003 self.global_rules[text_item] = rule
4004 self.list_of_rules_combobox.append_text(text_item)
4005 if len(rules) == 0:
4006 self.title_hbox.set_sensitive(False)
4007 self.list_of_rules_combobox.set_sensitive(False)
4008 self.delete_rule_button.set_sensitive(False)
4009 self.open_rule_button.set_sensitive(False)
4010 self.privacy_list_active_checkbutton.set_sensitive(False)
4011 self.privacy_list_default_checkbutton.set_sensitive(False)
4012 else:
4013 self.list_of_rules_combobox.set_active(0)
4014 self.title_hbox.set_sensitive(True)
4015 self.list_of_rules_combobox.set_sensitive(True)
4016 self.delete_rule_button.set_sensitive(True)
4017 self.open_rule_button.set_sensitive(True)
4018 self.privacy_list_active_checkbutton.set_sensitive(True)
4019 self.privacy_list_default_checkbutton.set_sensitive(True)
4020 self.reset_fields()
4021 gajim.connections[self.account].get_active_default_lists()
4023 def _nec_privacy_list_received(self, obj):
4024 if obj.conn.name != self.account:
4025 return
4026 if obj.list_name != self.privacy_list_name:
4027 return
4028 self.privacy_list_received(obj.rules)
4030 def refresh_rules(self):
4031 gajim.connections[self.account].get_privacy_list(self.privacy_list_name)
4033 def on_delete_rule_button_clicked(self, widget):
4034 tags = []
4035 for rule in self.global_rules:
4036 if rule != self.list_of_rules_combobox.get_active_text():
4037 tags.append(self.global_rules[rule])
4038 gajim.connections[self.account].set_privacy_list(
4039 self.privacy_list_name, tags)
4040 self.privacy_list_received(tags)
4041 self.add_edit_vbox.hide()
4042 if not tags: # we removed latest rule
4043 if 'privacy_lists' in gajim.interface.instances[self.account]:
4044 win = gajim.interface.instances[self.account]['privacy_lists']
4045 win.remove_privacy_list_from_combobox(self.privacy_list_name)
4046 win.draw_widgets()
4048 def on_open_rule_button_clicked(self, widget):
4049 self.add_edit_rule_label.set_label(
4050 _('<b>Edit a rule</b>'))
4051 active_num = self.list_of_rules_combobox.get_active()
4052 if active_num == -1:
4053 self.active_rule = ''
4054 else:
4055 self.active_rule = \
4056 self.list_of_rules_combobox.get_active_text().decode('utf-8')
4057 if self.active_rule != '':
4058 rule_info = self.global_rules[self.active_rule]
4059 self.edit_order_spinbutton.set_value(int(rule_info['order']))
4060 if 'type' in rule_info:
4061 if rule_info['type'] == 'jid':
4062 self.edit_type_jabberid_radiobutton.set_active(True)
4063 self.edit_type_jabberid_entry.set_text(rule_info['value'])
4064 elif rule_info['type'] == 'group':
4065 self.edit_type_group_radiobutton.set_active(True)
4066 if rule_info['value'] in self.list_of_groups:
4067 self.edit_type_group_combobox.set_active(
4068 self.list_of_groups[rule_info['value']])
4069 else:
4070 self.edit_type_group_combobox.set_active(0)
4071 elif rule_info['type'] == 'subscription':
4072 self.edit_type_subscription_radiobutton.set_active(True)
4073 sub_value = rule_info['value']
4074 if sub_value == 'none':
4075 self.edit_type_subscription_combobox.set_active(0)
4076 elif sub_value == 'both':
4077 self.edit_type_subscription_combobox.set_active(1)
4078 elif sub_value == 'from':
4079 self.edit_type_subscription_combobox.set_active(2)
4080 elif sub_value == 'to':
4081 self.edit_type_subscription_combobox.set_active(3)
4082 else:
4083 self.edit_type_select_all_radiobutton.set_active(True)
4084 else:
4085 self.edit_type_select_all_radiobutton.set_active(True)
4086 self.edit_send_messages_checkbutton.set_active(False)
4087 self.edit_queries_send_checkbutton.set_active(False)
4088 self.edit_view_status_checkbutton.set_active(False)
4089 self.edit_send_status_checkbutton.set_active(False)
4090 self.edit_all_checkbutton.set_active(False)
4091 if not rule_info['child']:
4092 self.edit_all_checkbutton.set_active(True)
4093 else:
4094 if 'presence-out' in rule_info['child']:
4095 self.edit_send_status_checkbutton.set_active(True)
4096 if 'presence-in' in rule_info['child']:
4097 self.edit_view_status_checkbutton.set_active(True)
4098 if 'iq' in rule_info['child']:
4099 self.edit_queries_send_checkbutton.set_active(True)
4100 if 'message' in rule_info['child']:
4101 self.edit_send_messages_checkbutton.set_active(True)
4103 if rule_info['action'] == 'allow':
4104 self.edit_allow_radiobutton.set_active(True)
4105 else:
4106 self.edit_deny_radiobutton.set_active(True)
4107 self.add_edit_vbox.show()
4109 def on_edit_all_checkbutton_toggled(self, widget):
4110 if widget.get_active():
4111 self.edit_send_messages_checkbutton.set_active(True)
4112 self.edit_queries_send_checkbutton.set_active(True)
4113 self.edit_view_status_checkbutton.set_active(True)
4114 self.edit_send_status_checkbutton.set_active(True)
4115 self.edit_send_messages_checkbutton.set_sensitive(False)
4116 self.edit_queries_send_checkbutton.set_sensitive(False)
4117 self.edit_view_status_checkbutton.set_sensitive(False)
4118 self.edit_send_status_checkbutton.set_sensitive(False)
4119 else:
4120 self.edit_send_messages_checkbutton.set_active(False)
4121 self.edit_queries_send_checkbutton.set_active(False)
4122 self.edit_view_status_checkbutton.set_active(False)
4123 self.edit_send_status_checkbutton.set_active(False)
4124 self.edit_send_messages_checkbutton.set_sensitive(True)
4125 self.edit_queries_send_checkbutton.set_sensitive(True)
4126 self.edit_view_status_checkbutton.set_sensitive(True)
4127 self.edit_send_status_checkbutton.set_sensitive(True)
4129 def on_privacy_list_active_checkbutton_toggled(self, widget):
4130 if widget.get_active():
4131 gajim.connections[self.account].set_active_list(
4132 self.privacy_list_name)
4133 else:
4134 gajim.connections[self.account].set_active_list(None)
4136 def on_privacy_list_default_checkbutton_toggled(self, widget):
4137 if widget.get_active():
4138 gajim.connections[self.account].set_default_list(
4139 self.privacy_list_name)
4140 else:
4141 gajim.connections[self.account].set_default_list(None)
4143 def on_new_rule_button_clicked(self, widget):
4144 self.reset_fields()
4145 self.add_edit_vbox.show()
4147 def reset_fields(self):
4148 self.edit_type_jabberid_entry.set_text('')
4149 self.edit_allow_radiobutton.set_active(True)
4150 self.edit_type_jabberid_radiobutton.set_active(True)
4151 self.active_rule = ''
4152 self.edit_send_messages_checkbutton.set_active(False)
4153 self.edit_queries_send_checkbutton.set_active(False)
4154 self.edit_view_status_checkbutton.set_active(False)
4155 self.edit_send_status_checkbutton.set_active(False)
4156 self.edit_all_checkbutton.set_active(False)
4157 self.edit_order_spinbutton.set_value(self.max_order + 1)
4158 self.edit_type_group_combobox.set_active(0)
4159 self.edit_type_subscription_combobox.set_active(0)
4160 self.add_edit_rule_label.set_label(
4161 _('<b>Add a rule</b>'))
4163 def get_current_tags(self):
4164 if self.edit_type_jabberid_radiobutton.get_active():
4165 edit_type = 'jid'
4166 edit_value = self.edit_type_jabberid_entry.get_text()
4167 elif self.edit_type_group_radiobutton.get_active():
4168 edit_type = 'group'
4169 edit_value = self.edit_type_group_combobox.get_active_text()
4170 elif self.edit_type_subscription_radiobutton.get_active():
4171 edit_type = 'subscription'
4172 subs = ['none', 'both', 'from', 'to']
4173 edit_value = subs[self.edit_type_subscription_combobox.get_active()]
4174 elif self.edit_type_select_all_radiobutton.get_active():
4175 edit_type = ''
4176 edit_value = ''
4177 edit_order = str(self.edit_order_spinbutton.get_value_as_int())
4178 if self.edit_allow_radiobutton.get_active():
4179 edit_deny = 'allow'
4180 else:
4181 edit_deny = 'deny'
4182 child = []
4183 if not self.edit_all_checkbutton.get_active():
4184 if self.edit_send_messages_checkbutton.get_active():
4185 child.append('message')
4186 if self.edit_queries_send_checkbutton.get_active():
4187 child.append('iq')
4188 if self.edit_send_status_checkbutton.get_active():
4189 child.append('presence-out')
4190 if self.edit_view_status_checkbutton.get_active():
4191 child.append('presence-in')
4192 if edit_type != '':
4193 return {'order': edit_order, 'action': edit_deny,
4194 'type': edit_type, 'value': edit_value, 'child': child}
4195 return {'order': edit_order, 'action': edit_deny, 'child': child}
4197 def on_save_rule_button_clicked(self, widget):
4198 tags=[]
4199 current_tags = self.get_current_tags()
4200 if int(current_tags['order']) > self.max_order:
4201 self.max_order = int(current_tags['order'])
4202 if self.active_rule == '':
4203 tags.append(current_tags)
4205 for rule in self.global_rules:
4206 if rule != self.active_rule:
4207 tags.append(self.global_rules[rule])
4208 else:
4209 tags.append(current_tags)
4211 gajim.connections[self.account].set_privacy_list(
4212 self.privacy_list_name, tags)
4213 self.refresh_rules()
4214 self.add_edit_vbox.hide()
4215 if 'privacy_lists' in gajim.interface.instances[self.account]:
4216 win = gajim.interface.instances[self.account]['privacy_lists']
4217 win.add_privacy_list_to_combobox(self.privacy_list_name)
4218 win.draw_widgets()
4220 def on_list_of_rules_combobox_changed(self, widget):
4221 self.add_edit_vbox.hide()
4223 def on_edit_type_radiobutton_changed(self, widget, radiobutton):
4224 active_bool = widget.get_active()
4225 if active_bool:
4226 self.edit_rule_type = radiobutton
4228 def on_edit_allow_radiobutton_changed(self, widget, radiobutton):
4229 active_bool = widget.get_active()
4230 if active_bool:
4231 self.allow_deny = radiobutton
4233 def on_close_button_clicked(self, widget):
4234 self.window.destroy()
4236 class PrivacyListsWindow:
4238 Window that is the main window for Privacy Lists; we can list there the
4239 privacy lists and ask to create a new one or edit an already there one
4241 def __init__(self, account):
4242 self.account = account
4243 self.privacy_lists_save = []
4245 self.xml = gtkgui_helpers.get_gtk_builder('privacy_lists_window.ui')
4247 self.window = self.xml.get_object('privacy_lists_first_window')
4248 for widget_to_add in ('list_of_privacy_lists_combobox',
4249 'delete_privacy_list_button', 'open_privacy_list_button',
4250 'new_privacy_list_button', 'new_privacy_list_entry',
4251 'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
4252 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
4254 self.draw_privacy_lists_in_combobox([])
4255 self.privacy_lists_refresh()
4257 self.enabled = True
4259 if len(gajim.connections) > 1:
4260 title = _('Privacy Lists for %s') % self.account
4261 else:
4262 title = _('Privacy Lists')
4264 self.window.set_title(title)
4266 gajim.ged.register_event_handler('privacy-lists-received', ged.GUI1,
4267 self._nec_privacy_lists_received)
4268 gajim.ged.register_event_handler('privacy-lists-removed', ged.GUI1,
4269 self._nec_privacy_lists_removed)
4271 self.window.show_all()
4273 self.xml.connect_signals(self)
4275 def on_privacy_lists_first_window_destroy(self, widget):
4276 if 'privacy_lists' in gajim.interface.instances[self.account]:
4277 del gajim.interface.instances[self.account]['privacy_lists']
4278 gajim.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
4279 self._nec_privacy_lists_received)
4280 gajim.ged.remove_event_handler('privacy-lists-removed', ged.GUI1,
4281 self._nec_privacy_lists_removed)
4283 def remove_privacy_list_from_combobox(self, privacy_list):
4284 if privacy_list not in self.privacy_lists_save:
4285 return
4286 privacy_list_index = self.privacy_lists_save.index(privacy_list)
4287 self.list_of_privacy_lists_combobox.remove_text(privacy_list_index)
4288 self.privacy_lists_save.remove(privacy_list)
4290 def add_privacy_list_to_combobox(self, privacy_list):
4291 if privacy_list in self.privacy_lists_save:
4292 return
4293 self.list_of_privacy_lists_combobox.append_text(privacy_list)
4294 self.privacy_lists_save.append(privacy_list)
4296 def draw_privacy_lists_in_combobox(self, privacy_lists):
4297 self.list_of_privacy_lists_combobox.set_active(-1)
4298 self.list_of_privacy_lists_combobox.get_model().clear()
4299 self.privacy_lists_save = []
4300 for add_item in privacy_lists:
4301 self.add_privacy_list_to_combobox(add_item)
4302 self.draw_widgets()
4304 def draw_widgets(self):
4305 if len(self.privacy_lists_save) == 0:
4306 self.list_of_privacy_lists_combobox.set_sensitive(False)
4307 self.open_privacy_list_button.set_sensitive(False)
4308 self.delete_privacy_list_button.set_sensitive(False)
4309 else:
4310 self.list_of_privacy_lists_combobox.set_sensitive(True)
4311 self.list_of_privacy_lists_combobox.set_active(0)
4312 self.open_privacy_list_button.set_sensitive(True)
4313 self.delete_privacy_list_button.set_sensitive(True)
4315 def on_close_button_clicked(self, widget):
4316 self.window.destroy()
4318 def on_delete_privacy_list_button_clicked(self, widget):
4319 active_list = self.privacy_lists_save[
4320 self.list_of_privacy_lists_combobox.get_active()]
4321 gajim.connections[self.account].del_privacy_list(active_list)
4323 def privacy_list_removed(self, active_list):
4324 self.privacy_lists_save.remove(active_list)
4325 self.privacy_lists_received({'lists': self.privacy_lists_save})
4327 def _nec_privacy_lists_removed(self, obj):
4328 if obj.conn.name != self.account:
4329 return
4330 self.privacy_list_removed(obj.lists_list)
4332 def privacy_lists_received(self, lists):
4333 if not lists:
4334 return
4335 privacy_lists = []
4336 for privacy_list in lists['lists']:
4337 privacy_lists.append(privacy_list)
4338 self.draw_privacy_lists_in_combobox(privacy_lists)
4340 def _nec_privacy_lists_received(self, obj):
4341 if obj.conn.name != self.account:
4342 return
4343 self.privacy_lists_received(obj.lists_list)
4345 def privacy_lists_refresh(self):
4346 gajim.connections[self.account].get_privacy_lists()
4348 def on_new_privacy_list_button_clicked(self, widget):
4349 name = self.new_privacy_list_entry.get_text()
4350 if not name:
4351 ErrorDialog(_('Invalid List Name'),
4352 _('You must enter a name to create a privacy list.'))
4353 return
4354 key_name = 'privacy_list_%s' % name
4355 if key_name in gajim.interface.instances[self.account]:
4356 gajim.interface.instances[self.account][key_name].window.present()
4357 else:
4358 gajim.interface.instances[self.account][key_name] = \
4359 PrivacyListWindow(self.account, name, 'NEW')
4360 self.new_privacy_list_entry.set_text('')
4362 def on_privacy_lists_refresh_button_clicked(self, widget):
4363 self.privacy_lists_refresh()
4365 def on_open_privacy_list_button_clicked(self, widget):
4366 name = self.privacy_lists_save[
4367 self.list_of_privacy_lists_combobox.get_active()]
4368 key_name = 'privacy_list_%s' % name
4369 if key_name in gajim.interface.instances[self.account]:
4370 gajim.interface.instances[self.account][key_name].window.present()
4371 else:
4372 gajim.interface.instances[self.account][key_name] = \
4373 PrivacyListWindow(self.account, name, 'EDIT')
4375 class InvitationReceivedDialog:
4376 def __init__(self, account, room_jid, contact_jid, password=None,
4377 comment=None, is_continued=False):
4379 self.room_jid = room_jid
4380 self.account = account
4381 self.password = password
4382 self.is_continued = is_continued
4384 pritext = _('''You are invited to a groupchat''')
4385 #Don't translate $Contact
4386 if is_continued:
4387 sectext = _('$Contact has invited you to join a discussion')
4388 else:
4389 sectext = _('$Contact has invited you to group chat %(room_jid)s')\
4390 % {'room_jid': room_jid}
4391 contact = gajim.contacts.get_first_contact_from_jid(account, contact_jid)
4392 contact_text = contact and contact.name or contact_jid
4393 sectext = sectext.replace('$Contact', contact_text)
4395 if comment: # only if not None and not ''
4396 comment = gobject.markup_escape_text(comment)
4397 comment = _('Comment: %s') % comment
4398 sectext += '\n\n%s' % comment
4399 sectext += '\n\n' + _('Do you want to accept the invitation?')
4401 def on_yes(checked):
4402 try:
4403 if self.is_continued:
4404 gajim.interface.join_gc_room(self.account, self.room_jid,
4405 gajim.nicks[self.account], None, is_continued=True)
4406 else:
4407 JoinGroupchatWindow(self.account, self.room_jid)
4408 except GajimGeneralException:
4409 pass
4411 YesNoDialog(pritext, sectext, on_response_yes=on_yes)
4413 class ProgressDialog:
4414 def __init__(self, title_text, during_text, messages_queue):
4416 During text is what to show during the procedure, messages_queue has the
4417 message to show in the textview
4419 self.xml = gtkgui_helpers.get_gtk_builder('progress_dialog.ui')
4420 self.dialog = self.xml.get_object('progress_dialog')
4421 self.label = self.xml.get_object('label')
4422 self.label.set_markup('<big>' + during_text + '</big>')
4423 self.progressbar = self.xml.get_object('progressbar')
4424 self.dialog.set_title(title_text)
4425 self.dialog.set_default_size(450, 250)
4426 self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
4427 self.dialog.show_all()
4428 self.xml.connect_signals(self)
4430 self.update_progressbar_timeout_id = gobject.timeout_add(100,
4431 self.update_progressbar)
4433 def update_progressbar(self):
4434 if self.dialog:
4435 self.progressbar.pulse()
4436 return True # loop forever
4437 return False
4439 def on_progress_dialog_delete_event(self, widget, event):
4440 return True # WM's X button or Escape key should not destroy the window
4443 class ClientCertChooserDialog(FileChooserDialog):
4444 def __init__(self, path_to_clientcert_file='', on_response_ok=None,
4445 on_response_cancel=None):
4447 optionally accepts path_to_clientcert_file so it has that as selected
4449 def on_ok(widget, callback):
4451 check if file exists and call callback
4453 path_to_clientcert_file = self.get_filename()
4454 path_to_clientcert_file = \
4455 gtkgui_helpers.decode_filechooser_file_paths(
4456 (path_to_clientcert_file,))[0]
4457 if os.path.exists(path_to_clientcert_file):
4458 callback(widget, path_to_clientcert_file)
4460 FileChooserDialog.__init__(self,
4461 title_text=_('Choose Client Cert #PCKS12'),
4462 action=gtk.FILE_CHOOSER_ACTION_OPEN,
4463 buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4464 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4465 current_folder='',
4466 default_response=gtk.RESPONSE_OK,
4467 on_response_ok=(on_ok, on_response_ok),
4468 on_response_cancel=on_response_cancel)
4470 filter_ = gtk.FileFilter()
4471 filter_.set_name(_('All files'))
4472 filter_.add_pattern('*')
4473 self.add_filter(filter_)
4475 filter_ = gtk.FileFilter()
4476 filter_.set_name(_('PKCS12 Files'))
4477 filter_.add_pattern('*.p12')
4478 self.add_filter(filter_)
4479 self.set_filter(filter_)
4481 if path_to_clientcert_file:
4482 # set_filename accept only absolute path
4483 path_to_clientcert_file = os.path.abspath(path_to_clientcert_file)
4484 self.set_filename(path_to_clientcert_file)
4487 class SoundChooserDialog(FileChooserDialog):
4488 def __init__(self, path_to_snd_file='', on_response_ok=None,
4489 on_response_cancel=None):
4491 Optionally accepts path_to_snd_file so it has that as selected
4493 def on_ok(widget, callback):
4495 Check if file exists and call callback
4497 path_to_snd_file = self.get_filename()
4498 path_to_snd_file = gtkgui_helpers.decode_filechooser_file_paths(
4499 (path_to_snd_file,))[0]
4500 if os.path.exists(path_to_snd_file):
4501 callback(widget, path_to_snd_file)
4503 FileChooserDialog.__init__(self, title_text = _('Choose Sound'),
4504 action = gtk.FILE_CHOOSER_ACTION_OPEN,
4505 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4506 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4507 default_response = gtk.RESPONSE_OK,
4508 current_folder = gajim.config.get('last_sounds_dir'),
4509 on_response_ok = (on_ok, on_response_ok),
4510 on_response_cancel = on_response_cancel)
4512 filter_ = gtk.FileFilter()
4513 filter_.set_name(_('All files'))
4514 filter_.add_pattern('*')
4515 self.add_filter(filter_)
4517 filter_ = gtk.FileFilter()
4518 filter_.set_name(_('Wav Sounds'))
4519 filter_.add_pattern('*.wav')
4520 self.add_filter(filter_)
4521 self.set_filter(filter_)
4523 path_to_snd_file = helpers.check_soundfile_path(path_to_snd_file)
4524 if path_to_snd_file:
4525 # set_filename accept only absolute path
4526 path_to_snd_file = os.path.abspath(path_to_snd_file)
4527 self.set_filename(path_to_snd_file)
4529 class ImageChooserDialog(FileChooserDialog):
4530 def __init__(self, path_to_file='', on_response_ok=None,
4531 on_response_cancel=None):
4533 Optionally accepts path_to_snd_file so it has that as selected
4535 def on_ok(widget, callback):
4536 '''check if file exists and call callback'''
4537 path_to_file = self.get_filename()
4538 if not path_to_file:
4539 return
4540 path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
4541 (path_to_file,))[0]
4542 if os.path.exists(path_to_file):
4543 if isinstance(callback, tuple):
4544 callback[0](widget, path_to_file, *callback[1:])
4545 else:
4546 callback(widget, path_to_file)
4548 try:
4549 if os.name == 'nt':
4550 path = helpers.get_my_pictures_path()
4551 else:
4552 path = os.environ['HOME']
4553 except Exception:
4554 path = ''
4555 FileChooserDialog.__init__(self,
4556 title_text = _('Choose Image'),
4557 action = gtk.FILE_CHOOSER_ACTION_OPEN,
4558 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4559 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4560 default_response = gtk.RESPONSE_OK,
4561 current_folder = path,
4562 on_response_ok = (on_ok, on_response_ok),
4563 on_response_cancel = on_response_cancel)
4565 if on_response_cancel:
4566 self.connect('destroy', on_response_cancel)
4568 filter_ = gtk.FileFilter()
4569 filter_.set_name(_('All files'))
4570 filter_.add_pattern('*')
4571 self.add_filter(filter_)
4573 filter_ = gtk.FileFilter()
4574 filter_.set_name(_('Images'))
4575 filter_.add_mime_type('image/png')
4576 filter_.add_mime_type('image/jpeg')
4577 filter_.add_mime_type('image/gif')
4578 filter_.add_mime_type('image/tiff')
4579 filter_.add_mime_type('image/svg+xml')
4580 filter_.add_mime_type('image/x-xpixmap') # xpm
4581 self.add_filter(filter_)
4582 self.set_filter(filter_)
4584 if path_to_file:
4585 self.set_filename(path_to_file)
4587 self.set_use_preview_label(False)
4588 self.set_preview_widget(gtk.Image())
4589 self.connect('selection-changed', self.update_preview)
4591 def update_preview(self, widget):
4592 path_to_file = widget.get_preview_filename()
4593 if path_to_file is None or os.path.isdir(path_to_file):
4594 # nothing to preview or directory
4595 # make sure you clean image do show nothing
4596 widget.get_preview_widget().set_from_file(None)
4597 return
4598 try:
4599 pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(path_to_file, 100, 100)
4600 except gobject.GError:
4601 return
4602 widget.get_preview_widget().set_from_pixbuf(pixbuf)
4604 class AvatarChooserDialog(ImageChooserDialog):
4605 def __init__(self, path_to_file='', on_response_ok=None,
4606 on_response_cancel=None, on_response_clear=None):
4607 ImageChooserDialog.__init__(self, path_to_file, on_response_ok,
4608 on_response_cancel)
4609 button = gtk.Button(None, gtk.STOCK_CLEAR)
4610 self.response_clear = on_response_clear
4611 if on_response_clear:
4612 button.connect('clicked', self.on_clear)
4613 button.show_all()
4614 self.action_area.pack_start(button)
4615 self.action_area.reorder_child(button, 0)
4617 def on_clear(self, widget):
4618 if isinstance(self.response_clear, tuple):
4619 self.response_clear[0](widget, *self.response_clear[1:])
4620 else:
4621 self.response_clear(widget)
4624 class ArchiveChooserDialog(FileChooserDialog):
4625 def __init__(self, on_response_ok=None, on_response_cancel=None):
4627 def on_ok(widget, callback):
4628 '''check if file exists and call callback'''
4629 path_to_file = self.get_filename()
4630 if not path_to_file:
4631 return
4632 path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
4633 (path_to_file,))[0]
4634 if os.path.exists(path_to_file):
4635 if isinstance(callback, tuple):
4636 callback[0](path_to_file, *callback[1:])
4637 else:
4638 callback(path_to_file)
4639 self.destroy()
4641 path = helpers.get_documents_path()
4643 FileChooserDialog.__init__(self,
4644 title_text=_('Choose Archive'),
4645 action=gtk.FILE_CHOOSER_ACTION_OPEN,
4646 buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4647 gtk.STOCK_OPEN, gtk.RESPONSE_OK),
4648 default_response=gtk.RESPONSE_OK,
4649 current_folder=path,
4650 on_response_ok=(on_ok, on_response_ok),
4651 on_response_cancel=on_response_cancel)
4653 if on_response_cancel:
4654 self.connect('destroy', on_response_cancel)
4656 filter_ = gtk.FileFilter()
4657 filter_.set_name(_('All files'))
4658 filter_.add_pattern('*')
4659 self.add_filter(filter_)
4661 filter_ = gtk.FileFilter()
4662 filter_.set_name(_('Zip files'))
4663 filter_.add_pattern('*.zip')
4665 self.add_filter(filter_)
4666 self.set_filter(filter_)
4669 class AddSpecialNotificationDialog:
4670 def __init__(self, jid):
4672 jid is the jid for which we want to add special notification (sound and
4673 notification popups)
4675 self.xml = gtkgui_helpers.get_gtk_builder(
4676 'add_special_notification_window.ui')
4677 self.window = self.xml.get_object('add_special_notification_window')
4678 self.condition_combobox = self.xml.get_object('condition_combobox')
4679 self.condition_combobox.set_active(0)
4680 self.notification_popup_yes_no_combobox = self.xml.get_object(
4681 'notification_popup_yes_no_combobox')
4682 self.notification_popup_yes_no_combobox.set_active(0)
4683 self.listen_sound_combobox = self.xml.get_object('listen_sound_combobox')
4684 self.listen_sound_combobox.set_active(0)
4686 self.jid = jid
4687 self.xml.get_object('when_foo_becomes_label').set_text(
4688 _('When %s becomes:') % self.jid)
4690 self.window.set_title(_('Adding Special Notification for %s') % jid)
4691 self.window.show_all()
4692 self.xml.connect_signals(self)
4694 def on_cancel_button_clicked(self, widget):
4695 self.window.destroy()
4697 def on_add_special_notification_window_delete_event(self, widget, event):
4698 self.window.destroy()
4700 def on_listen_sound_combobox_changed(self, widget):
4701 active = widget.get_active()
4702 if active == 1: # user selected 'choose sound'
4703 def on_ok(widget, path_to_snd_file):
4704 pass
4705 #print path_to_snd_file
4707 def on_cancel(widget):
4708 widget.set_active(0) # go back to No Sound
4710 self.dialog = SoundChooserDialog(on_response_ok=on_ok,
4711 on_response_cancel=on_cancel)
4713 def on_ok_button_clicked(self, widget):
4714 conditions = ('online', 'chat', 'online_and_chat',
4715 'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline')
4716 active = self.condition_combobox.get_active()
4718 active_iter = self.listen_sound_combobox.get_active_iter()
4719 listen_sound_model = self.listen_sound_combobox.get_model()
4721 class AdvancedNotificationsWindow:
4722 events_list = ['message_received', 'contact_connected',
4723 'contact_disconnected', 'contact_change_status', 'gc_msg_highlight',
4724 'gc_msg', 'ft_request', 'ft_started', 'ft_finished']
4725 recipient_types_list = ['contact', 'group', 'all']
4726 config_options = ['event', 'recipient_type', 'recipients', 'status',
4727 'tab_opened', 'sound', 'sound_file', 'popup', 'auto_open',
4728 'run_command', 'command', 'systray', 'roster', 'urgency_hint']
4729 def __init__(self):
4730 self.xml = gtkgui_helpers.get_gtk_builder(
4731 'advanced_notifications_window.ui')
4732 self.window = self.xml.get_object('advanced_notifications_window')
4733 for w in ('conditions_treeview', 'config_vbox', 'event_combobox',
4734 'recipient_type_combobox', 'recipient_list_entry', 'delete_button',
4735 'status_hbox', 'use_sound_cb', 'disable_sound_cb', 'use_popup_cb',
4736 'disable_popup_cb', 'use_auto_open_cb', 'disable_auto_open_cb',
4737 'use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
4738 'disable_roster_cb', 'tab_opened_cb', 'not_tab_opened_cb',
4739 'sound_entry', 'sound_file_hbox', 'up_button', 'down_button',
4740 'run_command_cb', 'command_entry', 'urgency_hint_cb'):
4741 self.__dict__[w] = self.xml.get_object(w)
4743 # Contains status checkboxes
4744 childs = self.status_hbox.get_children()
4746 self.all_status_rb = childs[0]
4747 self.special_status_rb = childs[1]
4748 self.online_cb = childs[2]
4749 self.away_cb = childs[3]
4750 self.xa_cb = childs[4]
4751 self.dnd_cb = childs[5]
4752 self.invisible_cb = childs[6]
4754 model = gtk.ListStore(int, str)
4755 model.set_sort_column_id(0, gtk.SORT_ASCENDING)
4756 model.clear()
4757 self.conditions_treeview.set_model(model)
4759 ## means number
4760 col = gtk.TreeViewColumn(_('#'))
4761 self.conditions_treeview.append_column(col)
4762 renderer = gtk.CellRendererText()
4763 col.pack_start(renderer, expand=False)
4764 col.set_attributes(renderer, text=0)
4766 col = gtk.TreeViewColumn(_('Condition'))
4767 self.conditions_treeview.append_column(col)
4768 renderer = gtk.CellRendererText()
4769 col.pack_start(renderer, expand=True)
4770 col.set_attributes(renderer, text=1)
4772 self.xml.connect_signals(self)
4774 # Fill conditions_treeview
4775 num = 0
4776 while gajim.config.get_per('notifications', str(num)):
4777 iter_ = model.append((num, ''))
4778 path = model.get_path(iter_)
4779 self.conditions_treeview.set_cursor(path)
4780 self.active_num = num
4781 self.initiate_rule_state()
4782 self.set_treeview_string()
4783 num += 1
4785 # No rule selected at init time
4786 self.conditions_treeview.get_selection().unselect_all()
4787 self.active_num = -1
4788 self.config_vbox.set_sensitive(False)
4789 self.delete_button.set_sensitive(False)
4790 self.down_button.set_sensitive(False)
4791 self.up_button.set_sensitive(False)
4793 self.window.show_all()
4795 def initiate_rule_state(self):
4797 Set values for all widgets
4799 if self.active_num < 0:
4800 return
4801 # event
4802 value = gajim.config.get_per('notifications', str(self.active_num),
4803 'event')
4804 if value:
4805 self.event_combobox.set_active(self.events_list.index(value))
4806 else:
4807 self.event_combobox.set_active(-1)
4808 # recipient_type
4809 value = gajim.config.get_per('notifications', str(self.active_num),
4810 'recipient_type')
4811 if value:
4812 self.recipient_type_combobox.set_active(
4813 self.recipient_types_list.index(value))
4814 else:
4815 self.recipient_type_combobox.set_active(-1)
4816 # recipient
4817 value = gajim.config.get_per('notifications', str(self.active_num),
4818 'recipients')
4819 if not value:
4820 value = ''
4821 self.recipient_list_entry.set_text(value)
4822 # status
4823 value = gajim.config.get_per('notifications', str(self.active_num),
4824 'status')
4825 if value == 'all':
4826 self.all_status_rb.set_active(True)
4827 else:
4828 self.special_status_rb.set_active(True)
4829 values = value.split()
4830 for v in ('online', 'away', 'xa', 'dnd', 'invisible'):
4831 if v in values:
4832 self.__dict__[v + '_cb'].set_active(True)
4833 else:
4834 self.__dict__[v + '_cb'].set_active(False)
4835 self.on_status_radiobutton_toggled(self.all_status_rb)
4836 # tab_opened
4837 value = gajim.config.get_per('notifications', str(self.active_num),
4838 'tab_opened')
4839 self.tab_opened_cb.set_active(True)
4840 self.not_tab_opened_cb.set_active(True)
4841 if value == 'no':
4842 self.tab_opened_cb.set_active(False)
4843 elif value == 'yes':
4844 self.not_tab_opened_cb.set_active(False)
4845 # sound_file
4846 value = gajim.config.get_per('notifications', str(self.active_num),
4847 'sound_file')
4848 self.sound_entry.set_text(value)
4849 # sound, popup, auto_open, systray, roster
4850 for option in ('sound', 'popup', 'auto_open', 'systray', 'roster'):
4851 value = gajim.config.get_per('notifications', str(self.active_num),
4852 option)
4853 if value == 'yes':
4854 self.__dict__['use_' + option + '_cb'].set_active(True)
4855 else:
4856 self.__dict__['use_' + option + '_cb'].set_active(False)
4857 if value == 'no':
4858 self.__dict__['disable_' + option + '_cb'].set_active(True)
4859 else:
4860 self.__dict__['disable_' + option + '_cb'].set_active(False)
4861 # run_command
4862 value = gajim.config.get_per('notifications', str(self.active_num),
4863 'run_command')
4864 self.run_command_cb.set_active(value)
4865 # command
4866 value = gajim.config.get_per('notifications', str(self.active_num),
4867 'command')
4868 self.command_entry.set_text(value)
4869 # urgency_hint
4870 value = gajim.config.get_per('notifications', str(self.active_num),
4871 'urgency_hint')
4872 self.urgency_hint_cb.set_active(value)
4874 def set_treeview_string(self):
4875 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4876 if not iter_:
4877 return
4878 event = self.event_combobox.get_active_text()
4879 recipient_type = self.recipient_type_combobox.get_active_text()
4880 recipient = ''
4881 if recipient_type != 'everybody':
4882 recipient = self.recipient_list_entry.get_text()
4883 if self.all_status_rb.get_active():
4884 status = ''
4885 else:
4886 status = _('when I am ')
4887 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
4888 if self.__dict__[st + '_cb'].get_active():
4889 status += helpers.get_uf_show(st) + ' '
4890 model[iter_][1] = "When %s for %s %s %s" % (event, recipient_type,
4891 recipient, status)
4893 def on_conditions_treeview_cursor_changed(self, widget):
4894 (model, iter_) = widget.get_selection().get_selected()
4895 if not iter_:
4896 self.active_num = -1
4897 return
4898 self.active_num = model[iter_][0]
4899 if self.active_num == 0:
4900 self.up_button.set_sensitive(False)
4901 else:
4902 self.up_button.set_sensitive(True)
4903 max = self.conditions_treeview.get_model().iter_n_children(None)
4904 if self.active_num == max - 1:
4905 self.down_button.set_sensitive(False)
4906 else:
4907 self.down_button.set_sensitive(True)
4908 self.initiate_rule_state()
4909 self.config_vbox.set_sensitive(True)
4910 self.delete_button.set_sensitive(True)
4912 def on_new_button_clicked(self, widget):
4913 model = self.conditions_treeview.get_model()
4914 num = self.conditions_treeview.get_model().iter_n_children(None)
4915 gajim.config.add_per('notifications', str(num))
4916 iter_ = model.append((num, ''))
4917 path = model.get_path(iter_)
4918 self.conditions_treeview.set_cursor(path)
4919 self.active_num = num
4920 self.set_treeview_string()
4921 self.config_vbox.set_sensitive(True)
4923 def on_delete_button_clicked(self, widget):
4924 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4925 if not iter_:
4926 return
4927 # up all others
4928 iter2 = model.iter_next(iter_)
4929 num = self.active_num
4930 while iter2:
4931 num = model[iter2][0]
4932 model[iter2][0] = num - 1
4933 for opt in self.config_options:
4934 val = gajim.config.get_per('notifications', str(num), opt)
4935 gajim.config.set_per('notifications', str(num - 1), opt, val)
4936 iter2 = model.iter_next(iter2)
4937 model.remove(iter_)
4938 gajim.config.del_per('notifications', str(num)) # delete latest
4939 self.active_num = -1
4940 self.config_vbox.set_sensitive(False)
4941 self.delete_button.set_sensitive(False)
4942 self.up_button.set_sensitive(False)
4943 self.down_button.set_sensitive(False)
4945 def on_up_button_clicked(self, widget):
4946 (model, iter_) = self.conditions_treeview.get_selection().\
4947 get_selected()
4948 if not iter_:
4949 return
4950 for opt in self.config_options:
4951 val = gajim.config.get_per('notifications', str(self.active_num),
4952 opt)
4953 val2 = gajim.config.get_per('notifications',
4954 str(self.active_num - 1), opt)
4955 gajim.config.set_per('notifications', str(self.active_num), opt,
4956 val2)
4957 gajim.config.set_per('notifications', str(self.active_num - 1), opt,
4958 val)
4960 model[iter_][0] = self.active_num - 1
4961 # get previous iter
4962 path = model.get_path(iter_)
4963 iter_ = model.get_iter((path[0] - 1,))
4964 model[iter_][0] = self.active_num
4965 self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
4967 def on_down_button_clicked(self, widget):
4968 (model, iter_) = self.conditions_treeview.get_selection().get_selected()
4969 if not iter_:
4970 return
4971 for opt in self.config_options:
4972 val = gajim.config.get_per('notifications', str(self.active_num),
4973 opt)
4974 val2 = gajim.config.get_per('notifications',
4975 str(self.active_num + 1), opt)
4976 gajim.config.set_per('notifications', str(self.active_num), opt,
4977 val2)
4978 gajim.config.set_per('notifications', str(self.active_num + 1), opt,
4979 val)
4981 model[iter_][0] = self.active_num + 1
4982 iter_ = model.iter_next(iter_)
4983 model[iter_][0] = self.active_num
4984 self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
4986 def on_event_combobox_changed(self, widget):
4987 if self.active_num < 0:
4988 return
4989 active = self.event_combobox.get_active()
4990 if active == -1:
4991 event = ''
4992 else:
4993 event = self.events_list[active]
4994 gajim.config.set_per('notifications', str(self.active_num), 'event',
4995 event)
4996 self.set_treeview_string()
4998 def on_recipient_type_combobox_changed(self, widget):
4999 if self.active_num < 0:
5000 return
5001 recipient_type = self.recipient_types_list[self.recipient_type_combobox.\
5002 get_active()]
5003 gajim.config.set_per('notifications', str(self.active_num),
5004 'recipient_type', recipient_type)
5005 if recipient_type == 'all':
5006 self.recipient_list_entry.hide()
5007 else:
5008 self.recipient_list_entry.show()
5009 self.set_treeview_string()
5011 def on_recipient_list_entry_changed(self, widget):
5012 if self.active_num < 0:
5013 return
5014 recipients = widget.get_text().decode('utf-8')
5015 #TODO: do some check
5016 gajim.config.set_per('notifications', str(self.active_num),
5017 'recipients', recipients)
5018 self.set_treeview_string()
5020 def set_status_config(self):
5021 if self.active_num < 0:
5022 return
5023 status = ''
5024 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
5025 if self.__dict__[st + '_cb'].get_active():
5026 status += st + ' '
5027 if status:
5028 status = status[:-1]
5029 gajim.config.set_per('notifications', str(self.active_num), 'status',
5030 status)
5031 self.set_treeview_string()
5033 def on_status_radiobutton_toggled(self, widget):
5034 if self.active_num < 0:
5035 return
5036 if self.all_status_rb.get_active():
5037 gajim.config.set_per('notifications', str(self.active_num), 'status',
5038 'all')
5039 # 'All status' clicked
5040 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
5041 self.__dict__[st + '_cb'].hide()
5043 self.special_status_rb.show()
5044 else:
5045 self.set_status_config()
5046 # 'special status' clicked
5047 for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
5048 self.__dict__[st + '_cb'].show()
5050 self.special_status_rb.hide()
5051 self.set_treeview_string()
5053 def on_status_cb_toggled(self, widget):
5054 if self.active_num < 0:
5055 return
5056 self.set_status_config()
5058 # tab_opened OR (not xor) not_tab_opened must be active
5059 def on_tab_opened_cb_toggled(self, widget):
5060 if self.active_num < 0:
5061 return
5062 if self.tab_opened_cb.get_active():
5063 if self.not_tab_opened_cb.get_active():
5064 gajim.config.set_per('notifications', str(self.active_num),
5065 'tab_opened', 'both')
5066 else:
5067 gajim.config.set_per('notifications', str(self.active_num),
5068 'tab_opened', 'yes')
5069 elif not self.not_tab_opened_cb.get_active():
5070 self.not_tab_opened_cb.set_active(True)
5071 gajim.config.set_per('notifications', str(self.active_num),
5072 'tab_opened', 'no')
5074 def on_not_tab_opened_cb_toggled(self, widget):
5075 if self.active_num < 0:
5076 return
5077 if self.not_tab_opened_cb.get_active():
5078 if self.tab_opened_cb.get_active():
5079 gajim.config.set_per('notifications', str(self.active_num),
5080 'tab_opened', 'both')
5081 else:
5082 gajim.config.set_per('notifications', str(self.active_num),
5083 'tab_opened', 'no')
5084 elif not self.tab_opened_cb.get_active():
5085 self.tab_opened_cb.set_active(True)
5086 gajim.config.set_per('notifications', str(self.active_num),
5087 'tab_opened', 'yes')
5089 def on_use_it_toggled(self, widget, oposite_widget, option):
5090 if widget.get_active():
5091 if oposite_widget.get_active():
5092 oposite_widget.set_active(False)
5093 gajim.config.set_per('notifications', str(self.active_num), option,
5094 'yes')
5095 elif oposite_widget.get_active():
5096 gajim.config.set_per('notifications', str(self.active_num), option,
5097 'no')
5098 else:
5099 gajim.config.set_per('notifications', str(self.active_num),
5100 option, '')
5102 def on_disable_it_toggled(self, widget, oposite_widget, option):
5103 if widget.get_active():
5104 if oposite_widget.get_active():
5105 oposite_widget.set_active(False)
5106 gajim.config.set_per('notifications', str(self.active_num), option,
5107 'no')
5108 elif oposite_widget.get_active():
5109 gajim.config.set_per('notifications', str(self.active_num), option,
5110 'yes')
5111 else:
5112 gajim.config.set_per('notifications', str(self.active_num), option,
5115 def on_use_sound_cb_toggled(self, widget):
5116 self.on_use_it_toggled(widget, self.disable_sound_cb, 'sound')
5117 if widget.get_active():
5118 self.sound_file_hbox.set_sensitive(True)
5119 else:
5120 self.sound_file_hbox.set_sensitive(False)
5122 def on_browse_for_sounds_button_clicked(self, widget, data=None):
5123 if self.active_num < 0:
5124 return
5126 def on_ok(widget, path_to_snd_file):
5127 dialog.destroy()
5128 if not path_to_snd_file:
5129 path_to_snd_file = ''
5130 gajim.config.set_per('notifications', str(self.active_num),
5131 'sound_file', path_to_snd_file)
5132 self.sound_entry.set_text(path_to_snd_file)
5134 path_to_snd_file = self.sound_entry.get_text().decode('utf-8')
5135 path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file)
5136 dialog = SoundChooserDialog(path_to_snd_file, on_ok)
5138 def on_play_button_clicked(self, widget):
5139 helpers.play_sound_file(self.sound_entry.get_text().decode('utf-8'))
5141 def on_disable_sound_cb_toggled(self, widget):
5142 self.on_disable_it_toggled(widget, self.use_sound_cb, 'sound')
5144 def on_sound_entry_changed(self, widget):
5145 gajim.config.set_per('notifications', str(self.active_num),
5146 'sound_file', widget.get_text().decode('utf-8'))
5148 def on_use_popup_cb_toggled(self, widget):
5149 self.on_use_it_toggled(widget, self.disable_popup_cb, 'popup')
5151 def on_disable_popup_cb_toggled(self, widget):
5152 self.on_disable_it_toggled(widget, self.use_popup_cb, 'popup')
5154 def on_use_auto_open_cb_toggled(self, widget):
5155 self.on_use_it_toggled(widget, self.disable_auto_open_cb, 'auto_open')
5157 def on_disable_auto_open_cb_toggled(self, widget):
5158 self.on_disable_it_toggled(widget, self.use_auto_open_cb, 'auto_open')
5160 def on_run_command_cb_toggled(self, widget):
5161 gajim.config.set_per('notifications', str(self.active_num),
5162 'run_command', widget.get_active())
5163 if widget.get_active():
5164 self.command_entry.set_sensitive(True)
5165 else:
5166 self.command_entry.set_sensitive(False)
5168 def on_command_entry_changed(self, widget):
5169 gajim.config.set_per('notifications', str(self.active_num), 'command',
5170 widget.get_text().decode('utf-8'))
5172 def on_use_systray_cb_toggled(self, widget):
5173 self.on_use_it_toggled(widget, self.disable_systray_cb, 'systray')
5175 def on_disable_systray_cb_toggled(self, widget):
5176 self.on_disable_it_toggled(widget, self.use_systray_cb, 'systray')
5178 def on_use_roster_cb_toggled(self, widget):
5179 self.on_use_it_toggled(widget, self.disable_roster_cb, 'roster')
5181 def on_disable_roster_cb_toggled(self, widget):
5182 self.on_disable_it_toggled(widget, self.use_roster_cb, 'roster')
5184 def on_urgency_hint_cb_toggled(self, widget):
5185 gajim.config.set_per('notifications', str(self.active_num),
5186 'uregency_hint', widget.get_active())
5188 def on_close_window(self, widget):
5189 self.window.destroy()
5191 class TransformChatToMUC:
5192 # Keep a reference on windows so garbage collector don't restroy them
5193 instances = []
5194 def __init__(self, account, jids, preselected=None):
5196 This window is used to trasform a one-to-one chat to a MUC. We do 2
5197 things: first select the server and then make a guests list
5200 self.instances.append(self)
5201 self.account = account
5202 self.auto_jids = jids
5203 self.preselected_jids = preselected
5205 self.xml = gtkgui_helpers.get_gtk_builder('chat_to_muc_window.ui')
5206 self.window = self.xml.get_object('chat_to_muc_window')
5208 for widget_to_add in ('invite_button', 'cancel_button',
5209 'server_list_comboboxentry', 'guests_treeview',
5210 'server_and_guests_hseparator', 'server_select_label'):
5211 self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
5213 server_list = []
5214 self.servers = gtk.ListStore(str)
5215 self.server_list_comboboxentry.set_model(self.servers)
5217 self.server_list_comboboxentry.set_text_column(0)
5219 # get the muc server of our server
5220 if 'jabber' in gajim.connections[account].muc_jid:
5221 server_list.append(gajim.connections[account].muc_jid['jabber'])
5222 # add servers or recently joined groupchats
5223 recently_groupchat = gajim.config.get('recently_groupchat').split()
5224 for g in recently_groupchat:
5225 server = gajim.get_server_from_jid(g)
5226 if server not in server_list and not server.startswith('irc'):
5227 server_list.append(server)
5228 # add a default server
5229 if not server_list:
5230 server_list.append('conference.jabber.org')
5232 for s in server_list:
5233 self.servers.append([s])
5235 self.server_list_comboboxentry.set_active(0)
5237 # set treeview
5238 # name, jid
5239 self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
5240 self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
5241 self.guests_treeview.set_model(self.store)
5243 renderer1 = gtk.CellRendererText()
5244 renderer2 = gtk.CellRendererPixbuf()
5245 column = gtk.TreeViewColumn('Status', renderer2, pixbuf=0)
5246 self.guests_treeview.append_column(column)
5247 column = gtk.TreeViewColumn('Name', renderer1, text=1)
5248 self.guests_treeview.append_column(column)
5250 self.guests_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
5252 # All contacts beside the following can be invited:
5253 # transports, zeroconf contacts, minimized groupchats
5254 def invitable(contact, contact_transport=None):
5255 return (contact.jid not in self.auto_jids and
5256 contact.jid != gajim.get_jid_from_account(self.account) and
5257 contact.jid not in gajim.interface.minimized_controls[account] and
5258 not contact.is_transport() and
5259 not contact_transport)
5261 # set jabber id and pseudos
5262 for account in gajim.contacts.get_accounts():
5263 if gajim.connections[account].is_zeroconf:
5264 continue
5265 for jid in gajim.contacts.get_jid_list(account):
5266 contact = gajim.contacts.get_contact_with_highest_priority(
5267 account, jid)
5268 contact_transport = gajim.get_transport_name_from_jid(jid)
5269 # Add contact if it can be invited
5270 if invitable(contact, contact_transport) and \
5271 contact.show not in ('offline', 'error'):
5272 img = gajim.interface.jabber_state_images['16'][contact.show]
5273 name = contact.name
5274 if name == '':
5275 name = jid.split('@')[0]
5276 iter_ = self.store.append([img.get_pixbuf(), name, jid])
5277 # preselect treeview rows
5278 if self.preselected_jids and jid in self.preselected_jids:
5279 path = self.store.get_path(iter_)
5280 self.guests_treeview.get_selection().select_path(path)
5282 gajim.ged.register_event_handler('unique-room-id-supported', ged.GUI1,
5283 self._nec_unique_room_id_supported)
5284 gajim.ged.register_event_handler('unique-room-id-not-supported',
5285 ged.GUI1, self._nec_unique_room_id_not_supported)
5287 # show all
5288 self.window.show_all()
5290 self.xml.connect_signals(self)
5292 def on_chat_to_muc_window_destroy(self, widget):
5293 gajim.ged.remove_event_handler('unique-room-id-supported', ged.GUI1,
5294 self._nec_unique_room_id_supported)
5295 gajim.ged.remove_event_handler('unique-room-id-not-supported', ged.GUI1,
5296 self._nec_unique_room_id_not_supported)
5297 self.instances.remove(self)
5299 def on_chat_to_muc_window_key_press_event(self, widget, event):
5300 if event.keyval == gtk.keysyms.Escape: # ESCAPE
5301 self.window.destroy()
5303 def on_invite_button_clicked(self, widget):
5304 server = self.server_list_comboboxentry.get_active_text()
5305 if server == '':
5306 return
5307 gajim.connections[self.account].check_unique_room_id_support(server, self)
5309 def _nec_unique_room_id_supported(self, obj):
5310 if obj.instance != self:
5311 return
5312 guest_list = []
5313 guests = self.guests_treeview.get_selection().get_selected_rows()
5314 for guest in guests[1]:
5315 iter_ = self.store.get_iter(guest)
5316 guest_list.append(self.store[iter_][2].decode('utf-8'))
5317 for guest in self.auto_jids:
5318 guest_list.append(guest)
5319 room_jid = obj.room_id + '@' + obj.server
5320 gajim.automatic_rooms[self.account][room_jid] = {}
5321 gajim.automatic_rooms[self.account][room_jid]['invities'] = guest_list
5322 gajim.automatic_rooms[self.account][room_jid]['continue_tag'] = True
5323 gajim.interface.join_gc_room(self.account, room_jid,
5324 gajim.nicks[self.account], None, is_continued=True)
5325 self.window.destroy()
5327 def on_cancel_button_clicked(self, widget):
5328 self.window.destroy()
5330 def _nec_unique_room_id_not_supported(self, obj):
5331 if obj.instance != self:
5332 return
5333 obj.room_id = gajim.nicks[self.account].lower().replace(' ', '') + \
5334 str(randrange(9999999))
5335 self._nec_unique_room_id_supported(obj)
5337 class DataFormWindow(Dialog):
5338 def __init__(self, form, on_response_ok):
5339 self.df_response_ok = on_response_ok
5340 Dialog.__init__(self, None, 'test', [(gtk.STOCK_CANCEL,
5341 gtk.RESPONSE_REJECT), (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)],
5342 on_response_ok=self.on_ok)
5343 self.set_resizable(True)
5344 gtkgui_helpers.resize_window(self, 600, 400)
5345 self.dataform_widget = dataforms_widget.DataFormWidget()
5346 self.dataform = dataforms.ExtendForm(node=form)
5347 self.dataform_widget.set_sensitive(True)
5348 self.dataform_widget.data_form = self.dataform
5349 self.dataform_widget.show_all()
5350 self.vbox.pack_start(self.dataform_widget)
5352 def on_ok(self):
5353 form = self.dataform_widget.data_form
5354 if isinstance(self.df_response_ok, tuple):
5355 self.df_response_ok[0](form, *self.df_response_ok[1:])
5356 else:
5357 self.df_response_ok(form)
5358 self.destroy()
5360 class ESessionInfoWindow:
5362 Class for displaying information about a XEP-0116 encrypted session
5364 def __init__(self, session):
5365 self.session = session
5367 self.xml = gtkgui_helpers.get_gtk_builder('esession_info_window.ui')
5368 self.xml.connect_signals(self)
5370 self.security_image = self.xml.get_object('security_image')
5371 self.verify_now_button = self.xml.get_object('verify_now_button')
5372 self.button_label = self.xml.get_object('button_label')
5373 self.window = self.xml.get_object('esession_info_window')
5374 self.update_info()
5377 self.window.show_all()
5379 def update_info(self):
5380 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}
5382 if self.session.verified_identity:
5383 labeltext += '\n\n' + _('''You have already verified this contact's identity.''')
5384 security_image = 'gajim-security_high'
5385 if self.session.control:
5386 self.session.control._show_lock_image(True, 'E2E', True,
5387 self.session.is_loggable(), True)
5389 verification_status = _('''Contact's identity verified''')
5390 self.window.set_title(verification_status)
5391 self.xml.get_object('verification_status_label').set_markup(
5392 '<b><span size="x-large">%s</span></b>' % verification_status)
5394 self.xml.get_object('dialog-action_area1').set_no_show_all(True)
5395 self.button_label.set_text(_('Verify again...'))
5396 else:
5397 if self.session.control:
5398 self.session.control._show_lock_image(True, 'E2E', True,
5399 self.session.is_loggable(), False)
5400 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.''')
5401 security_image = 'gajim-security_low'
5403 verification_status = _('''Contact's identity NOT verified''')
5404 self.window.set_title(verification_status)
5405 self.xml.get_object('verification_status_label').set_markup(
5406 '<b><span size="x-large">%s</span></b>' % verification_status)
5408 self.button_label.set_text(_('Verify...'))
5410 path = gtkgui_helpers.get_icon_path(security_image, 32)
5411 self.security_image.set_from_file(path)
5413 self.xml.get_object('info_display').set_markup(labeltext)
5415 def on_close_button_clicked(self, widget):
5416 self.window.destroy()
5418 def on_verify_now_button_clicked(self, widget):
5419 pritext = _('''Have you verified the contact's identity?''')
5420 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}
5421 sectext += '\n\n' + _('Did you talk to the remote contact and verify the SAS?')
5423 def on_yes(checked):
5424 self.session._verified_srs_cb()
5425 self.session.verified_identity = True
5426 self.update_info()
5428 def on_no():
5429 self.session._unverified_srs_cb()
5430 self.session.verified_identity = False
5431 self.update_info()
5433 YesNoDialog(pritext, sectext, on_response_yes=on_yes, on_response_no=on_no)
5435 class GPGInfoWindow:
5437 Class for displaying information about a XEP-0116 encrypted session
5439 def __init__(self, control):
5440 xml = gtkgui_helpers.get_gtk_builder('esession_info_window.ui')
5441 security_image = xml.get_object('security_image')
5442 status_label = xml.get_object('verification_status_label')
5443 info_label = xml.get_object('info_display')
5444 verify_now_button = xml.get_object('verify_now_button')
5445 self.window = xml.get_object('esession_info_window')
5446 account = control.account
5447 keyID = control.contact.keyID
5448 error = None
5450 verify_now_button.set_no_show_all(True)
5451 verify_now_button.hide()
5453 if keyID.endswith('MISMATCH'):
5454 verification_status = _('''Contact's identity NOT verified''')
5455 info = _('The contact\'s key (%s) <b>does not match</b> the key '
5456 'assigned in Gajim.') % keyID[:8]
5457 image = 'gajim-security_low'
5458 elif not keyID:
5459 # No key assigned nor a key is used by remote contact
5460 verification_status = _('No GPG key assigned')
5461 info = _('No GPG key is assigned to this contact. So you cannot '
5462 'encrypt messages.')
5463 image = 'gajim-security_low'
5464 else:
5465 error = gajim.connections[account].gpg.encrypt('test', [keyID])[1]
5466 if error:
5467 verification_status = _('''Contact's identity NOT verified''')
5468 info = _('GPG key is assigned to this contact, but <b>you do not '
5469 'trust his key</b>, so message <b>cannot</b> be encrypted. Use '
5470 'your GPG client to trust this key.')
5471 image = 'gajim-security_low'
5472 else:
5473 verification_status = _('''Contact's identity verified''')
5474 info = _('GPG Key is assigned to this contact, and you trust his '
5475 'key, so messages will be encrypted.')
5476 image = 'gajim-security_high'
5478 status_label.set_markup('<b><span size="x-large">%s</span></b>' % \
5479 verification_status)
5480 info_label.set_markup(info)
5482 path = gtkgui_helpers.get_icon_path(image, 32)
5483 security_image.set_from_file(path)
5485 xml.connect_signals(self)
5486 self.window.show_all()
5488 def on_close_button_clicked(self, widget):
5489 self.window.destroy()
5493 class ResourceConflictDialog(TimeoutDialog, InputDialog):
5494 def __init__(self, title, text, resource, ok_handler):
5495 TimeoutDialog.__init__(self, 15, self.on_timeout)
5496 InputDialog.__init__(self, title, text, input_str=resource,
5497 is_modal=False, ok_handler=ok_handler)
5498 self.title_text = title
5499 self.run_timeout()
5501 def on_timeout(self):
5502 self.on_okbutton_clicked(None)
5506 class VoIPCallReceivedDialog(object):
5507 instances = {}
5508 def __init__(self, account, contact_jid, sid, content_types):
5509 self.instances[(contact_jid, sid)] = self
5510 self.account = account
5511 self.fjid = contact_jid
5512 self.sid = sid
5513 self.content_types = content_types
5515 xml = gtkgui_helpers.get_gtk_builder('voip_call_received_dialog.ui')
5516 xml.connect_signals(self)
5518 jid = gajim.get_jid_without_resource(self.fjid)
5519 contact = gajim.contacts.get_first_contact_from_jid(account, jid)
5520 if contact and contact.name:
5521 self.contact_text = '%s (%s)' % (contact.name, jid)
5522 else:
5523 self.contact_text = contact_jid
5525 self.dialog = xml.get_object('voip_call_received_messagedialog')
5526 self.set_secondary_text()
5528 self.dialog.show_all()
5530 @classmethod
5531 def get_dialog(cls, jid, sid):
5532 if (jid, sid) in cls.instances:
5533 return cls.instances[(jid, sid)]
5534 else:
5535 return None
5537 def set_secondary_text(self):
5538 if 'audio' in self.content_types and 'video' in self.content_types:
5539 types_text = _('an audio and video')
5540 elif 'audio' in self.content_types:
5541 types_text = _('an audio')
5542 elif 'video' in self.content_types:
5543 types_text = _('a video')
5545 # do the substitution
5546 self.dialog.set_property('secondary-text',
5547 _('%(contact)s wants to start %(type)s session with you. Do you want '
5548 'to answer the call?') % {'contact': self.contact_text,
5549 'type': types_text})
5551 def add_contents(self, content_types):
5552 for type_ in content_types:
5553 if type_ not in self.content_types:
5554 self.content_types.add(type_)
5555 self.set_secondary_text()
5557 def remove_contents(self, content_types):
5558 for type_ in content_types:
5559 if type_ in self.content_types:
5560 self.content_types.remove(type_)
5561 if not self.content_types:
5562 self.dialog.destroy()
5563 else:
5564 self.set_secondary_text()
5566 def on_voip_call_received_messagedialog_destroy(self, dialog):
5567 if (self.fjid, self.sid) in self.instances:
5568 del self.instances[(self.fjid, self.sid)]
5570 def on_voip_call_received_messagedialog_close(self, dialog):
5571 return self.on_voip_call_received_messagedialog_response(dialog,
5572 gtk.RESPONSE_NO)
5574 def on_voip_call_received_messagedialog_response(self, dialog, response):
5575 # we've got response from user, either stop connecting or accept the call
5576 session = gajim.connections[self.account].get_jingle_session(self.fjid,
5577 self.sid)
5578 if not session:
5579 return
5580 if response == gtk.RESPONSE_YES:
5581 #TODO: Ensure that ctrl.contact.resource == resource
5582 jid = gajim.get_jid_without_resource(self.fjid)
5583 resource = gajim.get_resource_from_jid(self.fjid)
5584 ctrl = (gajim.interface.msg_win_mgr.get_control(self.fjid, self.account)
5585 or gajim.interface.msg_win_mgr.get_control(jid, self.account)
5586 or gajim.interface.new_chat_from_jid(self.account, jid))
5588 # Chat control opened, update content's status
5589 audio = session.get_content('audio')
5590 video = session.get_content('video')
5591 if audio and not audio.negotiated:
5592 ctrl.set_audio_state('connecting', self.sid)
5593 if video and not video.negotiated:
5594 ctrl.set_video_state('connecting', self.sid)
5595 # Now, accept the content/sessions.
5596 # This should be done after the chat control is running
5597 if not session.accepted:
5598 session.approve_session()
5599 for content in self.content_types:
5600 session.approve_content(content)
5601 else: # response==gtk.RESPONSE_NO
5602 if not session.accepted:
5603 session.decline_session()
5604 else:
5605 for content in self.content_types:
5606 session.reject_content(content)
5608 dialog.destroy()
5610 class CertificatDialog(InformationDialog):
5611 def __init__(self, parent, account, cert):
5612 issuer = cert.get_issuer()
5613 subject = cert.get_subject()
5614 InformationDialog.__init__(self,
5615 _('Certificate for account %s') % account, _('''<b>Issued to:</b>
5616 Common Name (CN): %(scn)s
5617 Organization (O): %(sorg)s
5618 Organizationl Unit (OU): %(sou)s
5619 Serial Number: %(sn)s
5621 <b>Issued by:</b>
5622 Common Name (CN): %(icn)s
5623 Organization (O): %(iorg)s
5624 Organizationl Unit (OU): %(iou)s
5626 <b>Validity:</b>
5627 Issued on: %(io)s
5628 Expires on: %(eo)s
5630 <b>Fingerprint</b>
5631 SHA1 Fingerprint: %(sha1)s''') % {
5632 'scn': subject.commonName, 'sorg': subject.organizationName,
5633 'sou': subject.organizationalUnitName,
5634 'sn': cert.get_serial_number(), 'icn': issuer.commonName,
5635 'iorg': issuer.organizationName,
5636 'iou': issuer.organizationalUnitName,
5637 'io': cert.get_notBefore(), 'eo': cert.get_notAfter(),
5638 'sha1': cert.digest('sha1')})
5639 self.set_transient_for(parent)
5642 class CheckFingerprintDialog(YesNoDialog):
5643 def __init__(self, pritext='', sectext='', checktext='',
5644 on_response_yes=None, on_response_no=None, account=None, certificate=None):
5645 self.account = account
5646 self.cert = certificate
5647 YesNoDialog.__init__(self, pritext, sectext, checktext, on_response_yes,
5648 on_response_no)
5649 b = gtk.Button('View cert...')
5650 b.connect('clicked', self.on_cert_clicked)
5651 b.show_all()
5652 area = self.get_action_area()
5653 area.pack_start(b)
5655 def on_cert_clicked(self, button):
5656 d = CertificatDialog(self, self.account, self.cert)