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