1 # -*- coding: utf-8 -*-
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/>.
38 import conversation_textview
39 import message_control
40 import dataforms_widget
42 from random
import randrange
43 from common
import pep
44 from common
import ged
52 # those imports are not used in this file, but in files that 'import dialogs'
53 # so they can do dialog.GajimThemesWindow() for example
54 from filetransfers_window
import FileTransfersWindow
55 from gajim_themes_window
import GajimThemesWindow
56 from advanced_configuration_window
import AdvancedConfigurationWindow
58 from common
import gajim
59 from common
import helpers
60 from common
import dataforms
61 from common
.exceptions
import GajimGeneralException
63 class EditGroupsDialog
:
65 Class for the edit group dialog window
68 def __init__(self
, list_
):
70 list_ is a list of (contact, account) tuples
72 self
.xml
= gtkgui_helpers
.get_gtk_builder('edit_groups_dialog.ui')
73 self
.dialog
= self
.xml
.get_object('edit_groups_dialog')
74 self
.dialog
.set_transient_for(gajim
.interface
.roster
.window
)
76 self
.changes_made
= False
77 self
.treeview
= self
.xml
.get_object('groups_treeview')
80 self
.xml
.get_object('nickname_label').set_markup(
81 _('Contact name: <i>%s</i>') % contact
.get_shown_name())
82 self
.xml
.get_object('jid_label').set_markup(
83 _('Jabber ID: <i>%s</i>') % contact
.jid
)
85 self
.xml
.get_object('nickname_label').set_no_show_all(True)
86 self
.xml
.get_object('nickname_label').hide()
87 self
.xml
.get_object('jid_label').set_no_show_all(True)
88 self
.xml
.get_object('jid_label').hide()
90 self
.xml
.connect_signals(self
)
93 self
.dialog
.show_all()
95 for (contact
, account
) in self
.list_
:
96 gajim
.connections
[account
].update_contact(contact
.jid
,
97 contact
.name
, contact
.groups
)
99 def on_edit_groups_dialog_response(self
, widget
, response_id
):
100 if response_id
== gtk
.RESPONSE_CLOSE
:
101 self
.dialog
.destroy()
103 def remove_group(self
, group
):
105 Remove group group from all contacts and all their brothers
107 for (contact
, account
) in self
.list_
:
108 gajim
.interface
.roster
.remove_contact_from_groups(contact
.jid
,
111 # FIXME: Ugly workaround.
112 gajim
.interface
.roster
.draw_group(_('General'), account
)
114 def add_group(self
, group
):
116 Add group group to all contacts and all their brothers
118 for (contact
, account
) in self
.list_
:
119 gajim
.interface
.roster
.add_contact_to_groups(contact
.jid
, account
,
122 # FIXME: Ugly workaround.
123 # Maybe we haven't been in any group (defaults to General)
124 gajim
.interface
.roster
.draw_group(_('General'), account
)
126 def on_add_button_clicked(self
, widget
):
127 group
= self
.xml
.get_object('group_entry').get_text().decode('utf-8')
130 # Do not allow special groups
131 if group
in helpers
.special_groups
:
133 # check if it already exists
134 model
= self
.treeview
.get_model()
135 iter_
= model
.get_iter_root()
137 if model
.get_value(iter_
, 0).decode('utf-8') == group
:
139 iter_
= model
.iter_next(iter_
)
140 self
.changes_made
= True
141 model
.append((group
, True, False))
142 self
.add_group(group
)
143 self
.init_list() # Re-draw list to sort new item
145 def group_toggled_cb(self
, cell
, path
):
146 self
.changes_made
= True
147 model
= self
.treeview
.get_model()
149 model
[path
][2] = False
150 model
[path
][1] = True
152 model
[path
][1] = not model
[path
][1]
153 group
= model
[path
][0].decode('utf-8')
155 self
.add_group(group
)
157 self
.remove_group(group
)
160 store
= gtk
.ListStore(str, bool, bool)
161 self
.treeview
.set_model(store
)
162 for column
in self
.treeview
.get_columns():
163 # Clear treeview when re-drawing
164 self
.treeview
.remove_column(column
)
166 # Store groups in a list so we can sort them and the number of contacts in
169 for (contact
, account
) in self
.list_
:
170 if account
not in accounts
:
171 accounts
.append(account
)
172 for g
in gajim
.groups
[account
].keys():
176 c_groups
= contact
.groups
180 # Remove special groups if they are empty
182 if group
not in helpers
.special_groups
or groups
[group
] > 0:
183 group_list
.append(group
)
185 for group
in group_list
:
186 iter_
= store
.append()
187 store
.set(iter_
, 0, group
) # Group name
188 if groups
[group
] == 0:
189 store
.set(iter_
, 1, False)
191 store
.set(iter_
, 1, True)
192 if groups
[group
] == len(self
.list_
):
193 # all contacts are in this group
194 store
.set(iter_
, 2, False)
196 store
.set(iter_
, 2, True)
197 column
= gtk
.TreeViewColumn(_('Group'))
198 column
.set_expand(True)
199 self
.treeview
.append_column(column
)
200 renderer
= gtk
.CellRendererText()
201 column
.pack_start(renderer
)
202 column
.set_attributes(renderer
, text
=0)
204 column
= gtk
.TreeViewColumn(_('In the group'))
205 column
.set_expand(False)
206 self
.treeview
.append_column(column
)
207 renderer
= gtk
.CellRendererToggle()
208 column
.pack_start(renderer
)
209 renderer
.set_property('activatable', True)
210 renderer
.connect('toggled', self
.group_toggled_cb
)
211 column
.set_attributes(renderer
, active
=1, inconsistent
=2)
213 class PassphraseDialog
:
215 Class for Passphrase dialog
217 def __init__(self
, titletext
, labeltext
, checkbuttontext
=None,
218 ok_handler
=None, cancel_handler
=None):
219 self
.xml
= gtkgui_helpers
.get_gtk_builder('passphrase_dialog.ui')
220 self
.window
= self
.xml
.get_object('passphrase_dialog')
221 self
.passphrase_entry
= self
.xml
.get_object('passphrase_entry')
223 self
.window
.set_title(titletext
)
224 self
.xml
.get_object('message_label').set_text(labeltext
)
228 self
.cancel_handler
= cancel_handler
229 self
.ok_handler
= ok_handler
230 okbutton
= self
.xml
.get_object('ok_button')
231 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
232 cancelbutton
= self
.xml
.get_object('cancel_button')
233 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
235 self
.xml
.connect_signals(self
)
236 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
237 self
.window
.show_all()
239 self
.check
= bool(checkbuttontext
)
240 checkbutton
= self
.xml
.get_object('save_passphrase_checkbutton')
242 checkbutton
.set_label(checkbuttontext
)
246 def on_okbutton_clicked(self
, widget
):
247 if not self
.ok_handler
:
250 passph
= self
.passphrase_entry
.get_text().decode('utf-8')
253 checked
= self
.xml
.get_object('save_passphrase_checkbutton').\
260 self
.window
.destroy()
262 if isinstance(self
.ok_handler
, tuple):
263 self
.ok_handler
[0](passph
, checked
, *self
.ok_handler
[1:])
265 self
.ok_handler(passph
, checked
)
267 def on_cancelbutton_clicked(self
, widget
):
268 self
.window
.destroy()
270 def on_passphrase_dialog_destroy(self
, widget
):
271 if self
.cancel_handler
and not self
.ok
:
272 self
.cancel_handler()
274 class ChooseGPGKeyDialog
:
276 Class for GPG key dialog
279 def __init__(self
, title_text
, prompt_text
, secret_keys
, on_response
,
281 '''secret_keys : {keyID: userName, ...}'''
282 self
.on_response
= on_response
283 xml
= gtkgui_helpers
.get_gtk_builder('choose_gpg_key_dialog.ui')
284 self
.window
= xml
.get_object('choose_gpg_key_dialog')
285 self
.window
.set_title(title_text
)
286 self
.keys_treeview
= xml
.get_object('keys_treeview')
287 prompt_label
= xml
.get_object('prompt_label')
288 prompt_label
.set_text(prompt_text
)
289 model
= gtk
.ListStore(str, str)
290 model
.set_sort_func(1, self
.sort_keys
)
291 model
.set_sort_column_id(1, gtk
.SORT_ASCENDING
)
292 self
.keys_treeview
.set_model(model
)
294 renderer
= gtk
.CellRendererText()
295 col
= self
.keys_treeview
.insert_column_with_attributes(-1, _('KeyID'),
297 col
.set_sort_column_id(0)
298 renderer
= gtk
.CellRendererText()
299 col
= self
.keys_treeview
.insert_column_with_attributes(-1,
300 _('Contact name'), renderer
, text
=1)
301 col
.set_sort_column_id(1)
302 self
.keys_treeview
.set_search_column(1)
303 self
.fill_tree(secret_keys
, selected
)
304 self
.window
.connect('response', self
.on_dialog_response
)
305 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
306 self
.window
.show_all()
308 def sort_keys(self
, model
, iter1
, iter2
):
309 value1
= model
[iter1
][1]
310 value2
= model
[iter2
][1]
311 if value1
== _('None'):
313 elif value2
== _('None'):
315 elif value1
< value2
:
319 def on_dialog_response(self
, dialog
, response
):
320 selection
= self
.keys_treeview
.get_selection()
321 (model
, iter_
) = selection
.get_selected()
322 if iter_
and response
== gtk
.RESPONSE_OK
:
323 keyID
= [ model
[iter_
][0].decode('utf-8'),
324 model
[iter_
][1].decode('utf-8') ]
327 self
.on_response(keyID
)
328 self
.window
.destroy()
330 def fill_tree(self
, list_
, selected
):
331 model
= self
.keys_treeview
.get_model()
332 for keyID
in list_
.keys():
333 iter_
= model
.append((keyID
, list_
[keyID
]))
334 if keyID
== selected
:
335 path
= model
.get_path(iter_
)
336 self
.keys_treeview
.set_cursor(path
)
339 class ChangeActivityDialog
:
340 PAGELIST
= ['doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
341 'having_appointment', 'inactive', 'relaxing', 'talking', 'traveling',
344 def __init__(self
, on_response
, activity
=None, subactivity
=None, text
=''):
345 self
.on_response
= on_response
346 self
.activity
= activity
347 self
.subactivity
= subactivity
349 self
.xml
= gtkgui_helpers
.get_gtk_builder('change_activity_dialog.ui')
350 self
.window
= self
.xml
.get_object('change_activity_dialog')
351 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
353 self
.checkbutton
= self
.xml
.get_object('enable_checkbutton')
354 self
.notebook
= self
.xml
.get_object('notebook')
355 self
.entry
= self
.xml
.get_object('description_entry')
360 for category
in pep
.ACTIVITIES
:
361 item
= self
.xml
.get_object(category
+ '_image')
362 item
.set_from_pixbuf(
363 gtkgui_helpers
.load_activity_icon(category
).get_pixbuf())
364 item
.set_tooltip_text(pep
.ACTIVITIES
[category
]['category'])
366 vbox
= self
.xml
.get_object(category
+ '_vbox')
367 vbox
.set_border_width(5)
370 act
= category
+ '_other'
373 rbtns
[act
] = gtk
.RadioButton(group
)
375 rbtns
[act
] = group
= gtk
.RadioButton()
377 hbox
= gtk
.HBox(False, 5)
378 hbox
.pack_start(gtkgui_helpers
.load_activity_icon(category
), False,
380 lbl
= gtk
.Label('<b>' + pep
.ACTIVITIES
[category
]['category'] + '</b>')
381 lbl
.set_use_markup(True)
382 hbox
.pack_start(lbl
, False, False, 0)
384 rbtns
[act
].connect('toggled', self
.on_rbtn_toggled
,
386 vbox
.pack_start(rbtns
[act
], False, False, 0)
389 for activity
in pep
.ACTIVITIES
[category
]:
390 activities
.append(activity
)
393 for activity
in activities
:
394 if activity
== 'category':
397 act
= category
+ '_' + activity
400 rbtns
[act
] = gtk
.RadioButton(group
)
402 rbtns
[act
] = group
= gtk
.RadioButton()
404 hbox
= gtk
.HBox(False, 5)
405 hbox
.pack_start(gtkgui_helpers
.load_activity_icon(category
,
406 activity
), False, False, 0)
407 hbox
.pack_start(gtk
.Label(pep
.ACTIVITIES
[category
][activity
]),
409 rbtns
[act
].connect('toggled', self
.on_rbtn_toggled
,
410 [category
, activity
])
412 vbox
.pack_start(rbtns
[act
], False, False, 0)
415 self
.default_radio
= rbtns
['doing_chores_other']
417 if self
.activity
in pep
.ACTIVITIES
:
418 if not self
.subactivity
in pep
.ACTIVITIES
[self
.activity
]:
419 self
.subactivity
= 'other'
421 rbtns
[self
.activity
+ '_' + self
.subactivity
].set_active(True)
423 self
.checkbutton
.set_active(True)
424 self
.notebook
.set_sensitive(True)
425 self
.entry
.set_sensitive(True)
427 self
.notebook
.set_current_page(
428 self
.PAGELIST
.index(self
.activity
))
430 self
.entry
.set_text(text
)
433 self
.checkbutton
.set_active(False)
435 self
.xml
.connect_signals(self
)
436 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
437 self
.window
.show_all()
439 def on_enable_checkbutton_toggled(self
, widget
):
440 self
.notebook
.set_sensitive(widget
.get_active())
441 self
.entry
.set_sensitive(widget
.get_active())
442 if not self
.activity
:
443 self
.default_radio
.set_active(True)
445 def on_rbtn_toggled(self
, widget
, data
):
446 if widget
.get_active():
447 self
.activity
= data
[0]
448 self
.subactivity
= data
[1]
450 def on_ok_button_clicked(self
, widget
):
452 Return activity and messsage (None if no activity selected)
454 if self
.checkbutton
.get_active():
455 self
.on_response(self
.activity
, self
.subactivity
,
456 self
.entry
.get_text().decode('utf-8'))
458 self
.on_response(None, None, '')
459 self
.window
.destroy()
461 def on_cancel_button_clicked(self
, widget
):
462 self
.window
.destroy()
464 class ChangeMoodDialog
:
467 def __init__(self
, on_response
, mood
=None, text
=''):
468 self
.on_response
= on_response
471 self
.xml
= gtkgui_helpers
.get_gtk_builder('change_mood_dialog.ui')
473 self
.window
= self
.xml
.get_object('change_mood_dialog')
474 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
475 self
.window
.set_title(_('Set Mood'))
477 table
= self
.xml
.get_object('mood_icons_table')
478 self
.label
= self
.xml
.get_object('mood_label')
479 self
.entry
= self
.xml
.get_object('description_entry')
481 no_mood_button
= self
.xml
.get_object('no_mood_button')
482 no_mood_button
.set_mode(False)
483 no_mood_button
.connect('clicked',
484 self
.on_mood_button_clicked
, None)
488 self
.mood_buttons
= {}
492 for mood
in pep
.MOODS
:
493 self
.MOODS
.append(mood
)
496 for mood
in self
.MOODS
:
497 self
.mood_buttons
[mood
] = gtk
.RadioButton(no_mood_button
)
498 self
.mood_buttons
[mood
].set_mode(False)
499 self
.mood_buttons
[mood
].add(gtkgui_helpers
.load_mood_icon(mood
))
500 self
.mood_buttons
[mood
].set_relief(gtk
.RELIEF_NONE
)
501 self
.mood_buttons
[mood
].set_tooltip_text(pep
.MOODS
[mood
])
502 self
.mood_buttons
[mood
].connect('clicked',
503 self
.on_mood_button_clicked
, mood
)
504 table
.attach(self
.mood_buttons
[mood
], x
, x
+ 1, y
, y
+ 1)
506 # Calculate the next position
512 if self
.mood
in pep
.MOODS
:
513 self
.mood_buttons
[self
.mood
].set_active(True)
514 self
.label
.set_text(pep
.MOODS
[self
.mood
])
515 self
.entry
.set_sensitive(True)
517 self
.entry
.set_text(self
.text
)
519 self
.label
.set_text(_('None'))
520 self
.entry
.set_text('')
521 self
.entry
.set_sensitive(False)
523 self
.xml
.connect_signals(self
)
524 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
525 self
.window
.show_all()
527 def on_mood_button_clicked(self
, widget
, data
):
529 self
.label
.set_text(pep
.MOODS
[data
])
530 self
.entry
.set_sensitive(True)
532 self
.label
.set_text(_('None'))
533 self
.entry
.set_text('')
534 self
.entry
.set_sensitive(False)
537 def on_ok_button_clicked(self
, widget
):
538 '''Return mood and messsage (None if no mood selected)'''
539 message
= self
.entry
.get_text().decode('utf-8')
540 self
.on_response(self
.mood
, message
)
541 self
.window
.destroy()
543 def on_cancel_button_clicked(self
, widget
):
544 self
.window
.destroy()
548 Class designed to be derivated to create timeout'd dialogs (dialogs that
549 closes automatically after a timeout)
551 def __init__(self
, timeout
, on_timeout
):
552 self
.countdown_left
= timeout
553 self
.countdown_enabled
= True
555 self
.on_timeout
= on_timeout
557 def run_timeout(self
):
558 if self
.countdown_left
> 0:
560 gobject
.timeout_add_seconds(1, self
.countdown
)
564 To be implemented in derivated classes
569 if self
.countdown_enabled
:
570 if self
.countdown_left
<= 0:
573 self
.dialog
.set_title('%s [%s]' % (self
.title_text
,
574 str(self
.countdown_left
)))
575 self
.countdown_left
-= 1
578 self
.dialog
.set_title(self
.title_text
)
581 class ChangeStatusMessageDialog(TimeoutDialog
):
582 def __init__(self
, on_response
, show
=None, show_pep
=True):
583 countdown_time
= gajim
.config
.get('change_status_window_timeout')
584 TimeoutDialog
.__init
__(self
, countdown_time
, self
.on_timeout
)
587 self
.show_pep
= show_pep
588 self
.on_response
= on_response
589 self
.xml
= gtkgui_helpers
.get_gtk_builder('change_status_message_dialog.ui')
590 self
.dialog
= self
.xml
.get_object('change_status_message_dialog')
591 self
.dialog
.set_transient_for(gajim
.interface
.roster
.window
)
594 uf_show
= helpers
.get_uf_show(show
)
595 self
.title_text
= _('%s Status Message') % uf_show
596 msg
= gajim
.config
.get_per('statusmsg', '_last_' + self
.show
,
598 self
.pep_dict
['activity'] = gajim
.config
.get_per('statusmsg',
599 '_last_' + self
.show
, 'activity')
600 self
.pep_dict
['subactivity'] = gajim
.config
.get_per('statusmsg',
601 '_last_' + self
.show
, 'subactivity')
602 self
.pep_dict
['activity_text'] = gajim
.config
.get_per('statusmsg',
603 '_last_' + self
.show
, 'activity_text')
604 self
.pep_dict
['mood'] = gajim
.config
.get_per('statusmsg',
605 '_last_' + self
.show
, 'mood')
606 self
.pep_dict
['mood_text'] = gajim
.config
.get_per('statusmsg',
607 '_last_' + self
.show
, 'mood_text')
609 self
.title_text
= _('Status Message')
610 self
.dialog
.set_title(self
.title_text
)
612 message_textview
= self
.xml
.get_object('message_textview')
613 self
.message_buffer
= message_textview
.get_buffer()
614 self
.message_buffer
.connect('changed', self
.on_message_buffer_changed
)
617 msg
= helpers
.from_one_line(msg
)
618 self
.message_buffer
.set_text(msg
)
620 # have an empty string selectable, so user can clear msg
621 self
.preset_messages_dict
= {'': ['', '', '', '', '', '']}
622 for msg_name
in gajim
.config
.get_per('statusmsg'):
623 if msg_name
.startswith('_last_'):
626 for opt
in ['message', 'activity', 'subactivity', 'activity_text',
627 'mood', 'mood_text']:
628 opts
.append(gajim
.config
.get_per('statusmsg', msg_name
, opt
))
629 opts
[0] = helpers
.from_one_line(opts
[0])
630 self
.preset_messages_dict
[msg_name
] = opts
631 sorted_keys_list
= helpers
.get_sorted_keys(self
.preset_messages_dict
)
633 self
.message_liststore
= gtk
.ListStore(str) # msg_name
634 self
.message_combobox
= self
.xml
.get_object('message_combobox')
635 self
.message_combobox
.set_model(self
.message_liststore
)
636 cellrenderertext
= gtk
.CellRendererText()
637 self
.message_combobox
.pack_start(cellrenderertext
, True)
638 self
.message_combobox
.add_attribute(cellrenderertext
, 'text', 0)
639 for msg_name
in sorted_keys_list
:
640 self
.message_liststore
.append((msg_name
,))
646 # remove acvtivity / mood lines
647 self
.xml
.get_object('activity_label').set_no_show_all(True)
648 self
.xml
.get_object('activity_button').set_no_show_all(True)
649 self
.xml
.get_object('mood_label').set_no_show_all(True)
650 self
.xml
.get_object('mood_button').set_no_show_all(True)
651 self
.xml
.get_object('activity_label').hide()
652 self
.xml
.get_object('activity_button').hide()
653 self
.xml
.get_object('mood_label').hide()
654 self
.xml
.get_object('mood_button').hide()
656 self
.xml
.connect_signals(self
)
658 self
.dialog
.connect('response', self
.on_dialog_response
)
659 self
.dialog
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
660 self
.dialog
.show_all()
662 def draw_activity(self
):
666 img
= self
.xml
.get_object('activity_image')
667 label
= self
.xml
.get_object('activity_button_label')
668 if 'activity' in self
.pep_dict
and self
.pep_dict
['activity'] in \
670 if 'subactivity' in self
.pep_dict
and self
.pep_dict
['subactivity'] \
671 in pep
.ACTIVITIES
[self
.pep_dict
['activity']]:
672 img
.set_from_pixbuf(gtkgui_helpers
.load_activity_icon(
673 self
.pep_dict
['activity'], self
.pep_dict
['subactivity']).\
676 img
.set_from_pixbuf(gtkgui_helpers
.load_activity_icon(
677 self
.pep_dict
['activity']).get_pixbuf())
678 if self
.pep_dict
['activity_text']:
679 label
.set_text(self
.pep_dict
['activity_text'])
683 img
.set_from_pixbuf(None)
690 img
= self
.xml
.get_object('mood_image')
691 label
= self
.xml
.get_object('mood_button_label')
692 if 'mood' in self
.pep_dict
and self
.pep_dict
['mood'] in pep
.MOODS
:
693 img
.set_from_pixbuf(gtkgui_helpers
.load_mood_icon(
694 self
.pep_dict
['mood']).get_pixbuf())
695 if self
.pep_dict
['mood_text']:
696 label
.set_text(self
.pep_dict
['mood_text'])
700 img
.set_from_pixbuf(None)
703 def on_timeout(self
):
704 # Prevent GUI freeze when the combobox menu is opened on close
705 self
.message_combobox
.popdown()
706 self
.dialog
.response(gtk
.RESPONSE_OK
)
708 def on_dialog_response(self
, dialog
, response
):
709 if response
== gtk
.RESPONSE_OK
:
710 beg
, end
= self
.message_buffer
.get_bounds()
711 message
= self
.message_buffer
.get_text(beg
, end
).decode('utf-8')\
713 message
= helpers
.remove_invalid_xml_chars(message
)
714 msg
= helpers
.to_one_line(message
)
716 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
719 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
720 'activity', self
.pep_dict
['activity'])
721 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
722 'subactivity', self
.pep_dict
['subactivity'])
723 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
724 'activity_text', self
.pep_dict
['activity_text'])
725 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
726 'mood', self
.pep_dict
['mood'])
727 gajim
.config
.set_per('statusmsg', '_last_' + self
.show
,
728 'mood_text', self
.pep_dict
['mood_text'])
730 message
= None # user pressed Cancel button or X wm button
731 self
.dialog
.destroy()
732 self
.on_response(message
, self
.pep_dict
)
734 def on_message_combobox_changed(self
, widget
):
735 self
.countdown_enabled
= False
736 model
= widget
.get_model()
737 active
= widget
.get_active()
740 name
= model
[active
][0].decode('utf-8')
741 self
.message_buffer
.set_text(self
.preset_messages_dict
[name
][0])
742 self
.pep_dict
['activity'] = self
.preset_messages_dict
[name
][1]
743 self
.pep_dict
['subactivity'] = self
.preset_messages_dict
[name
][2]
744 self
.pep_dict
['activity_text'] = self
.preset_messages_dict
[name
][3]
745 self
.pep_dict
['mood'] = self
.preset_messages_dict
[name
][4]
746 self
.pep_dict
['mood_text'] = self
.preset_messages_dict
[name
][5]
750 def on_change_status_message_dialog_key_press_event(self
, widget
, event
):
751 self
.countdown_enabled
= False
752 if event
.keyval
== gtk
.keysyms
.Return
or \
753 event
.keyval
== gtk
.keysyms
.KP_Enter
: # catch CTRL+ENTER
754 if (event
.state
& gtk
.gdk
.CONTROL_MASK
):
755 self
.dialog
.response(gtk
.RESPONSE_OK
)
759 def on_message_buffer_changed(self
, widget
):
760 self
.countdown_enabled
= False
761 self
.toggle_sensitiviy_of_save_as_preset()
763 def toggle_sensitiviy_of_save_as_preset(self
):
764 btn
= self
.xml
.get_object('save_as_preset_button')
765 if self
.message_buffer
.get_char_count() == 0:
766 btn
.set_sensitive(False)
768 btn
.set_sensitive(True)
770 def on_save_as_preset_button_clicked(self
, widget
):
771 self
.countdown_enabled
= False
772 start_iter
, finish_iter
= self
.message_buffer
.get_bounds()
773 status_message_to_save_as_preset
= self
.message_buffer
.get_text(
774 start_iter
, finish_iter
)
776 msg_text
= status_message_to_save_as_preset
.decode('utf-8')
777 msg_text_1l
= helpers
.to_one_line(msg_text
)
778 if not msg_name
: # msg_name was ''
779 msg_name
= msg_text_1l
.decode('utf-8')
782 self
.preset_messages_dict
[msg_name
] = [
783 msg_text
, self
.pep_dict
.get('activity'),
784 self
.pep_dict
.get('subactivity'),
785 self
.pep_dict
.get('activity_text'),
786 self
.pep_dict
.get('mood'), self
.pep_dict
.get('mood_text')]
787 gajim
.config
.set_per('statusmsg', msg_name
, 'message',
789 gajim
.config
.set_per('statusmsg', msg_name
, 'activity',
790 self
.pep_dict
.get('activity'))
791 gajim
.config
.set_per('statusmsg', msg_name
, 'subactivity',
792 self
.pep_dict
.get('subactivity'))
793 gajim
.config
.set_per('statusmsg', msg_name
, 'activity_text',
794 self
.pep_dict
.get('activity_text'))
795 gajim
.config
.set_per('statusmsg', msg_name
, 'mood',
796 self
.pep_dict
.get('mood'))
797 gajim
.config
.set_per('statusmsg', msg_name
, 'mood_text',
798 self
.pep_dict
.get('mood_text'))
799 if msg_name
in self
.preset_messages_dict
:
800 ConfirmationDialog(_('Overwrite Status Message?'),
801 _('This name is already used. Do you want to overwrite this '
802 'status message?'), on_response_ok
=on_ok2
)
804 gajim
.config
.add_per('statusmsg', msg_name
)
806 iter_
= self
.message_liststore
.append((msg_name
,))
807 # select in combobox the one we just saved
808 self
.message_combobox
.set_active_iter(iter_
)
809 InputDialog(_('Save as Preset Status Message'),
810 _('Please type a name for this status message'), is_modal
=False,
813 def on_activity_button_clicked(self
, widget
):
814 self
.countdown_enabled
= False
815 def on_response(activity
, subactivity
, text
):
816 self
.pep_dict
['activity'] = activity
or ''
817 self
.pep_dict
['subactivity'] = subactivity
or ''
818 self
.pep_dict
['activity_text'] = text
820 ChangeActivityDialog(on_response
, self
.pep_dict
['activity'],
821 self
.pep_dict
['subactivity'], self
.pep_dict
['activity_text'])
823 def on_mood_button_clicked(self
, widget
):
824 self
.countdown_enabled
= False
825 def on_response(mood
, text
):
826 self
.pep_dict
['mood'] = mood
or ''
827 self
.pep_dict
['mood_text'] = text
829 ChangeMoodDialog(on_response
, self
.pep_dict
['mood'],
830 self
.pep_dict
['mood_text'])
832 class AddNewContactWindow
:
834 Class for AddNewContactWindow
837 uid_labels
= {'jabber': _('Jabber ID:'),
838 'aim': _('AIM Address:'),
839 'gadu-gadu': _('GG Number:'),
840 'icq': _('ICQ Number:'),
841 'msn': _('MSN Address:'),
842 'yahoo': _('Yahoo! Address:')}
844 def __init__(self
, account
=None, jid
=None, user_nick
=None, group
=None):
845 self
.account
= account
846 self
.adding_jid
= False
848 # fill accounts with active accounts
850 for account
in gajim
.connections
.keys():
851 if gajim
.connections
[account
].connected
> 1:
852 accounts
.append(account
)
855 if len(accounts
) == 1:
856 self
.account
= account
858 accounts
= [self
.account
]
860 location
= gajim
.interface
.instances
[self
.account
]
862 location
= gajim
.interface
.instances
863 if 'add_contact' in location
:
864 location
['add_contact'].window
.present()
865 # An instance is already opened
867 location
['add_contact'] = self
868 self
.xml
= gtkgui_helpers
.get_gtk_builder('add_new_contact_window.ui')
869 self
.xml
.connect_signals(self
)
870 self
.window
= self
.xml
.get_object('add_new_contact_window')
871 for w
in ('account_combobox', 'account_hbox', 'account_label',
872 'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
873 'protocol_hbox', 'nickname_entry', 'message_scrolledwindow',
874 'save_message_checkbutton', 'register_hbox', 'subscription_table',
875 'add_button', 'message_textview', 'connected_label',
876 'group_comboboxentry', 'auto_authorize_checkbutton'):
877 self
.__dict
__[w
] = self
.xml
.get_object(w
)
878 if account
and len(gajim
.connections
) >= 2:
879 self
.default_desc
= _('Please fill in the data of the contact you '
880 'want to add in account %s') % account
882 self
.default_desc
= _('Please fill in the data of the contact you '
884 self
.xml
.get_object('prompt_label').set_text(self
.default_desc
)
885 self
.agents
= {'jabber': []}
886 self
.gateway_prompt
= {}
887 # types to which we are not subscribed but account has an agent for it
888 self
.available_types
= []
889 for acct
in accounts
:
890 for j
in gajim
.contacts
.get_jid_list(acct
):
891 if gajim
.jid_is_transport(j
):
892 type_
= gajim
.get_transport_name_from_jid(j
, False)
895 if type_
in self
.agents
:
896 self
.agents
[type_
].append(j
)
898 self
.agents
[type_
] = [j
]
899 self
.gateway_prompt
[j
] = {'desc': None, 'prompt': None}
900 # Now add the one to which we can register
901 for acct
in accounts
:
902 for type_
in gajim
.connections
[acct
].available_transports
:
903 if type_
in self
.agents
:
905 self
.agents
[type_
] = []
906 for jid_
in gajim
.connections
[acct
].available_transports
[type_
]:
907 if not jid_
in self
.agents
[type_
]:
908 self
.agents
[type_
].append(jid_
)
909 self
.gateway_prompt
[jid_
] = {'desc': None,
911 self
.available_types
.append(type_
)
912 # Combobox with transport/jabber icons
913 liststore
= gtk
.ListStore(str, gtk
.gdk
.Pixbuf
, str)
914 cell
= gtk
.CellRendererPixbuf()
915 self
.protocol_combobox
.pack_start(cell
, False)
916 self
.protocol_combobox
.add_attribute(cell
, 'pixbuf', 1)
917 cell
= gtk
.CellRendererText()
918 cell
.set_property('xpad', 5)
919 self
.protocol_combobox
.pack_start(cell
, True)
920 self
.protocol_combobox
.add_attribute(cell
, 'text', 0)
921 self
.protocol_combobox
.set_model(liststore
)
922 uf_type
= {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
923 'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
925 img
= gajim
.interface
.jabber_state_images
['16']['online']
926 liststore
.append(['Jabber', img
.get_pixbuf(), 'jabber'])
927 for type_
in self
.agents
:
928 if type_
== 'jabber':
930 imgs
= gajim
.interface
.roster
.transports_state_images
932 if type_
in imgs
['16'] and 'online' in imgs
['16'][type_
]:
933 img
= imgs
['16'][type_
]['online']
935 liststore
.append([uf_type
[type_
], img
.get_pixbuf(), type_
])
937 liststore
.append([type_
, img
.get_pixbuf(), type_
])
939 liststore
.append([type_
, img
, type_
])
941 for service
in self
.agents
[type_
]:
942 gajim
.connections
[account
].request_gateway_prompt(service
)
943 self
.protocol_combobox
.set_active(0)
944 self
.auto_authorize_checkbutton
.show()
945 liststore
= gtk
.ListStore(str)
946 self
.protocol_jid_combobox
.set_model(liststore
)
948 type_
= gajim
.get_transport_name_from_jid(jid
)
951 if type_
== 'jabber':
952 self
.uid_entry
.set_text(jid
)
954 uid
, transport
= gajim
.get_name_and_server_from_jid(jid
)
955 self
.uid_entry
.set_text(uid
.replace('%', '@', 1))
956 # set protocol_combobox
957 model
= self
.protocol_combobox
.get_model()
958 iter_
= model
.get_iter_first()
961 if model
[iter_
][2] == type_
:
962 self
.protocol_combobox
.set_active(i
)
964 iter_
= model
.iter_next(iter_
)
967 # set protocol_jid_combobox
968 self
.protocol_jid_combobox
.set_active(0)
969 model
= self
.protocol_jid_combobox
.get_model()
970 iter_
= model
.get_iter_first()
973 if model
[iter_
][0] == transport
:
974 self
.protocol_jid_combobox
.set_active(i
)
976 iter_
= model
.iter_next(iter_
)
979 self
.nickname_entry
.set_text(user_nick
)
980 self
.nickname_entry
.grab_focus()
982 self
.uid_entry
.grab_focus()
984 for acct
in accounts
:
985 for g
in gajim
.groups
[acct
].keys():
986 if g
not in helpers
.special_groups
and g
not in group_names
:
987 group_names
.append(g
)
990 for g
in group_names
:
991 self
.group_comboboxentry
.append_text(g
)
993 self
.group_comboboxentry
.set_active(i
)
996 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
997 self
.window
.show_all()
1000 self
.account_label
.hide()
1001 self
.account_hbox
.hide()
1003 liststore
= gtk
.ListStore(str, str)
1004 for acct
in accounts
:
1005 liststore
.append([acct
, acct
])
1006 self
.account_combobox
.set_model(liststore
)
1007 self
.account_combobox
.set_active(0)
1010 message_buffer
= self
.message_textview
.get_buffer()
1011 msg
= helpers
.from_one_line(helpers
.get_subscription_request_msg(
1013 message_buffer
.set_text(msg
)
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
):
1022 location
= gajim
.interface
.instances
[self
.account
]
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()
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(
1060 self
.adding_jid
= (jid
, transport
, type_
)
1061 gajim
.connections
[self
.account
].request_gateway_prompt(
1064 jid
= jid
.replace('@', '%') + '@' + transport
1065 self
._add
_jid
(jid
, type_
)
1067 self
._add
_jid
(jid
, type_
)
1069 def _add_jid(self
, jid
, type_
):
1070 # check if jid is conform to RFC and stringprep it
1072 jid
= helpers
.parse_jid(jid
)
1073 except helpers
.InvalidFormat
, s
:
1074 pritext
= _('Invalid User ID')
1075 ErrorDialog(pritext
, str(s
))
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.'))
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.'))
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.'))
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 msg
= helpers
.to_one_line(message
)
1111 gajim
.config
.set_per('accounts', self
.account
,
1112 'subscription_request_msg', msg
)
1115 group
= self
.group_comboboxentry
.child
.get_text().decode('utf-8')
1119 auto_auth
= self
.auto_authorize_checkbutton
.get_active()
1120 gajim
.interface
.roster
.req_sub(self
, jid
, message
, self
.account
,
1121 groups
=groups
, nickname
=nickname
, auto_auth
=auto_auth
)
1122 self
.window
.destroy()
1124 def on_account_combobox_changed(self
, widget
):
1125 model
= widget
.get_model()
1126 iter_
= widget
.get_active_iter()
1127 account
= model
[iter_
][0]
1128 message_buffer
= self
.message_textview
.get_buffer()
1129 message_buffer
.set_text(helpers
.get_subscription_request_msg(account
))
1131 def on_protocol_jid_combobox_changed(self
, widget
):
1132 model
= widget
.get_model()
1133 iter_
= widget
.get_active_iter()
1136 jid_
= model
[iter_
][0]
1137 model
= self
.protocol_combobox
.get_model()
1138 iter_
= self
.protocol_combobox
.get_active_iter()
1139 type_
= model
[iter_
][2]
1141 if self
.agents
[type_
] and jid_
in self
.gateway_prompt
:
1142 desc
= self
.gateway_prompt
[jid_
]['desc']
1144 desc
= self
.default_desc
1145 self
.xml
.get_object('prompt_label').set_text(desc
)
1148 if self
.agents
[type_
] and jid_
in self
.gateway_prompt
:
1149 prompt
= self
.gateway_prompt
[jid_
]['prompt']
1151 if type_
in self
.uid_labels
:
1152 prompt
= self
.uid_labels
[type_
]
1154 prompt
= _('User ID:')
1155 self
.uid_label
.set_text(prompt
)
1157 def on_protocol_combobox_changed(self
, widget
):
1158 model
= widget
.get_model()
1159 iter_
= widget
.get_active_iter()
1160 type_
= model
[iter_
][2]
1161 model
= self
.protocol_jid_combobox
.get_model()
1163 if len(self
.agents
[type_
]):
1164 for jid_
in self
.agents
[type_
]:
1165 model
.append([jid_
])
1166 self
.protocol_jid_combobox
.set_active(0)
1168 if self
.agents
[type_
]:
1169 jid_
= self
.agents
[type_
][0]
1170 if jid_
in self
.gateway_prompt
:
1171 desc
= self
.gateway_prompt
[jid_
]['desc']
1173 desc
= self
.default_desc
1174 self
.xml
.get_object('prompt_label').set_text(desc
)
1175 if len(self
.agents
[type_
]) > 1:
1176 self
.protocol_jid_combobox
.show()
1178 self
.protocol_jid_combobox
.hide()
1180 if self
.agents
[type_
]:
1181 jid_
= self
.agents
[type_
][0]
1182 if jid_
in self
.gateway_prompt
:
1183 prompt
= self
.gateway_prompt
[jid_
]['prompt']
1185 if type_
in self
.uid_labels
:
1186 prompt
= self
.uid_labels
[type_
]
1188 prompt
= _('User ID:')
1189 self
.uid_label
.set_text(prompt
)
1191 if type_
== 'jabber':
1192 self
.message_scrolledwindow
.show()
1193 self
.save_message_checkbutton
.show()
1195 self
.message_scrolledwindow
.hide()
1196 self
.save_message_checkbutton
.hide()
1197 if type_
in self
.available_types
:
1198 self
.register_hbox
.show()
1199 self
.auto_authorize_checkbutton
.hide()
1200 self
.connected_label
.hide()
1201 self
.subscription_table
.hide()
1202 self
.add_button
.set_sensitive(False)
1204 self
.register_hbox
.hide()
1205 if type_
!= 'jabber':
1206 jid
= self
.protocol_jid_combobox
.get_active_text()
1207 contact
= gajim
.contacts
.get_first_contact_from_jid(
1209 if contact
.show
in ('offline', 'error'):
1210 self
.subscription_table
.hide()
1211 self
.connected_label
.show()
1212 self
.add_button
.set_sensitive(False)
1213 self
.auto_authorize_checkbutton
.hide()
1215 self
.subscription_table
.show()
1216 self
.auto_authorize_checkbutton
.show()
1217 self
.connected_label
.hide()
1218 self
.add_button
.set_sensitive(True)
1220 def transport_signed_in(self
, jid
):
1221 if self
.protocol_jid_combobox
.get_active_text() == jid
:
1222 self
.register_hbox
.hide()
1223 self
.connected_label
.hide()
1224 self
.subscription_table
.show()
1225 self
.auto_authorize_checkbutton
.show()
1226 self
.add_button
.set_sensitive(True)
1228 def transport_signed_out(self
, jid
):
1229 if self
.protocol_jid_combobox
.get_active_text() == jid
:
1230 self
.subscription_table
.hide()
1231 self
.auto_authorize_checkbutton
.hide()
1232 self
.connected_label
.show()
1233 self
.add_button
.set_sensitive(False)
1235 def _nec_presence_received(self
, obj
):
1236 if gajim
.jid_is_transport(obj
.jid
):
1237 if obj
.old_show
== 0 and obj
.new_show
> 1:
1238 self
.transport_signed_in(obj
.jid
)
1239 elif obj
.old_show
> 1 and obj
.new_show
== 0:
1240 self
.transport_signed_out(obj
.jid
)
1242 def _nec_gateway_prompt_received(self
, obj
):
1244 jid
, transport
, type_
= self
.adding_jid
1246 self
._add
_jid
(obj
.prompt_jid
, type_
)
1248 jid
= jid
.replace('@', '%') + '@' + transport
1249 self
._add
_jid
(jid
, type_
)
1250 elif obj
.jid
in self
.gateway_prompt
:
1252 self
.gateway_prompt
[obj
.jid
]['desc'] = obj
.desc
1254 self
.gateway_prompt
[obj
.jid
]['prompt'] = obj
.prompt
1258 Class for about dialog
1262 dlg
= gtk
.AboutDialog()
1263 dlg
.set_transient_for(gajim
.interface
.roster
.window
)
1264 dlg
.set_name('Gajim')
1265 dlg
.set_version(gajim
.version
)
1266 s
= u
'Copyright © 2003-2011 Gajim Team'
1267 dlg
.set_copyright(s
)
1268 copying_file_path
= self
.get_path('COPYING')
1269 if copying_file_path
:
1270 text
= open(copying_file_path
).read()
1271 dlg
.set_license(text
)
1273 dlg
.set_comments('%s\n%s %s\n%s %s' % (_('A GTK+ jabber client'),
1274 _('GTK+ Version:'), self
.tuple2str(gtk
.gtk_version
), \
1275 _('PyGTK Version:'), self
.tuple2str(gtk
.pygtk_version
)))
1276 dlg
.set_website('http://www.gajim.org/')
1278 authors_file_path
= self
.get_path('AUTHORS')
1279 if authors_file_path
:
1281 authors_file
= open(authors_file_path
).read()
1282 authors_file
= authors_file
.split('\n')
1283 for author
in authors_file
:
1284 if author
== 'CURRENT DEVELOPERS:':
1285 authors
.append(_('Current Developers:'))
1286 elif author
== 'PAST DEVELOPERS:':
1287 authors
.append('\n' + _('Past Developers:'))
1288 elif author
!= '': # Real author line
1289 authors
.append(author
)
1291 thanks_file_path
= self
.get_path('THANKS')
1292 if thanks_file_path
:
1293 authors
.append('\n' + _('THANKS:'))
1295 text
= open(thanks_file_path
).read()
1296 text_splitted
= text
.split('\n')
1297 text
= '\n'.join(text_splitted
[:-2]) # remove one english sentence
1298 # and add it manually as translatable
1299 text
+= '\n%s\n' % _('Last but not least, we would like to thank all '
1300 'the package maintainers.')
1301 authors
.append(text
)
1303 dlg
.set_authors(authors
)
1305 dlg
.props
.wrap_license
= True
1307 pixbuf
= gtkgui_helpers
.get_icon_pixmap('gajim-about', 128)
1309 dlg
.set_logo(pixbuf
)
1310 #here you write your name in the form Name FamilyName <someone@somewhere>
1311 dlg
.set_translator_credits(_('translator-credits'))
1313 thanks_artists_file_path
= self
.get_path('THANKS.artists')
1314 if thanks_artists_file_path
:
1315 artists_text
= open(thanks_artists_file_path
).read()
1316 artists
= artists_text
.split('\n')
1317 dlg
.set_artists(artists
)
1318 # connect close button to destroy() function
1319 for button
in dlg
.action_area
.get_children():
1320 if button
.get_property('label') == gtk
.STOCK_CLOSE
:
1321 button
.connect('clicked', lambda x
:dlg
.destroy())
1324 def tuple2str(self
, tuple_
):
1327 str_
+= str(num
) + '.'
1328 return str_
[0:-1] # remove latest .
1330 def get_path(self
, filename
):
1332 Where can we find this Credits file?
1334 if os
.path
.isfile(os
.path
.join(gajim
.defs
.docdir
, filename
)):
1335 return os
.path
.join(gajim
.defs
.docdir
, filename
)
1336 elif os
.path
.isfile('../' + filename
):
1337 return ('../' + filename
)
1341 class Dialog(gtk
.Dialog
):
1342 def __init__(self
, parent
, title
, buttons
, default
=None,
1343 on_response_ok
=None, on_response_cancel
=None):
1344 gtk
.Dialog
.__init
__(self
, title
, parent
,
1345 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_NO_SEPARATOR
)
1347 self
.user_response_ok
= on_response_ok
1348 self
.user_response_cancel
= on_response_cancel
1349 self
.set_border_width(6)
1350 self
.vbox
.set_spacing(12)
1351 self
.set_resizable(False)
1353 possible_responses
= {gtk
.STOCK_OK
: self
.on_response_ok
,
1354 gtk
.STOCK_CANCEL
: self
.on_response_cancel
}
1355 for stock
, response
in buttons
:
1356 b
= self
.add_button(stock
, response
)
1357 for response
in possible_responses
:
1358 if stock
== response
:
1359 b
.connect('clicked', possible_responses
[response
])
1362 if default
is not None:
1363 self
.set_default_response(default
)
1365 self
.set_default_response(buttons
[-1][1])
1367 def on_response_ok(self
, widget
):
1368 if self
.user_response_ok
:
1369 if isinstance(self
.user_response_ok
, tuple):
1370 self
.user_response_ok
[0](*self
.user_response_ok
[1:])
1372 self
.user_response_ok()
1375 def on_response_cancel(self
, widget
):
1376 if self
.user_response_cancel
:
1377 if isinstance(self
.user_response_cancel
, tuple):
1378 self
.user_response_cancel
[0](*self
.user_response_ok
[1:])
1380 self
.user_response_cancel()
1383 def just_destroy(self
, widget
):
1386 def get_button(self
, index
):
1387 buttons
= self
.action_area
.get_children()
1388 return index
< len(buttons
) and buttons
[index
] or None
1391 class HigDialog(gtk
.MessageDialog
):
1392 def __init__(self
, parent
, type_
, buttons
, pritext
, sectext
,
1393 on_response_ok
=None, on_response_cancel
=None, on_response_yes
=None,
1394 on_response_no
=None):
1395 self
.call_cancel_on_destroy
= True
1396 gtk
.MessageDialog
.__init
__(self
, parent
,
1397 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
1398 type_
, buttons
, message_format
= pritext
)
1400 self
.format_secondary_markup(sectext
)
1402 buttons
= self
.action_area
.get_children()
1403 self
.possible_responses
= {gtk
.STOCK_OK
: on_response_ok
,
1404 gtk
.STOCK_CANCEL
: on_response_cancel
, gtk
.STOCK_YES
: on_response_yes
,
1405 gtk
.STOCK_NO
: on_response_no
}
1407 for response
in self
.possible_responses
:
1408 if b
.get_label() == response
:
1409 if not self
.possible_responses
[response
]:
1410 b
.connect('clicked', self
.just_destroy
)
1411 elif isinstance(self
.possible_responses
[response
], tuple):
1412 if len(self
.possible_responses
[response
]) == 1:
1413 b
.connect('clicked', self
.possible_responses
[response
][0])
1415 b
.connect('clicked', *self
.possible_responses
[response
])
1417 b
.connect('clicked', self
.possible_responses
[response
])
1420 self
.connect('destroy', self
.on_dialog_destroy
)
1422 def on_dialog_destroy(self
, widget
):
1423 if not self
.call_cancel_on_destroy
:
1425 cancel_handler
= self
.possible_responses
[gtk
.STOCK_CANCEL
]
1426 if not cancel_handler
:
1428 if isinstance(cancel_handler
, tuple):
1429 cancel_handler
[0](None, *cancel_handler
[1:])
1431 cancel_handler(None)
1433 def just_destroy(self
, widget
):
1440 vb
= self
.get_children()[0].get_children()[0] # Give focus to top vbox
1441 vb
.set_flags(gtk
.CAN_FOCUS
)
1445 class FileChooserDialog(gtk
.FileChooserDialog
):
1447 Non-blocking FileChooser Dialog around gtk.FileChooserDialog
1449 def __init__(self
, title_text
, action
, buttons
, default_response
,
1450 select_multiple
=False, current_folder
=None, on_response_ok
=None,
1451 on_response_cancel
=None):
1453 gtk
.FileChooserDialog
.__init
__(self
, title
=title_text
, action
=action
,
1456 self
.set_default_response(default_response
)
1457 self
.set_select_multiple(select_multiple
)
1458 if current_folder
and os
.path
.isdir(current_folder
):
1459 self
.set_current_folder(current_folder
)
1461 self
.set_current_folder(helpers
.get_documents_path())
1462 self
.response_ok
, self
.response_cancel
= \
1463 on_response_ok
, on_response_cancel
1464 # in gtk+-2.10 clicked signal on some of the buttons in a dialog
1465 # is emitted twice, so we cannot rely on 'clicked' signal
1466 self
.connect('response', self
.on_dialog_response
)
1469 def on_dialog_response(self
, dialog
, response
):
1470 if response
in (gtk
.RESPONSE_CANCEL
, gtk
.RESPONSE_CLOSE
):
1471 if self
.response_cancel
:
1472 if isinstance(self
.response_cancel
, tuple):
1473 self
.response_cancel
[0](dialog
, *self
.response_cancel
[1:])
1475 self
.response_cancel(dialog
)
1477 self
.just_destroy(dialog
)
1478 elif response
== gtk
.RESPONSE_OK
:
1479 if self
.response_ok
:
1480 if isinstance(self
.response_ok
, tuple):
1481 self
.response_ok
[0](dialog
, *self
.response_ok
[1:])
1483 self
.response_ok(dialog
)
1485 self
.just_destroy(dialog
)
1487 def just_destroy(self
, widget
):
1490 class AspellDictError
:
1491 def __init__(self
, lang
):
1493 _('Dictionary for lang %s not available') % lang
,
1494 _('You have to install %s dictionary to use spellchecking, or '
1495 'choose another language by setting the speller_language option.'
1496 '\n\nHighlighting misspelled words feature will not be used') % lang
)
1497 gajim
.config
.set('use_speller', False)
1499 class ConfirmationDialog(HigDialog
):
1501 HIG compliant confirmation dialog
1504 def __init__(self
, pritext
, sectext
='', on_response_ok
=None,
1505 on_response_cancel
=None):
1506 self
.user_response_ok
= on_response_ok
1507 self
.user_response_cancel
= on_response_cancel
1508 HigDialog
.__init
__(self
, None,
1509 gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
,
1510 self
.on_response_ok
, self
.on_response_cancel
)
1513 def on_response_ok(self
, widget
):
1514 if self
.user_response_ok
:
1515 if isinstance(self
.user_response_ok
, tuple):
1516 self
.user_response_ok
[0](*self
.user_response_ok
[1:])
1518 self
.user_response_ok()
1519 self
.call_cancel_on_destroy
= False
1522 def on_response_cancel(self
, widget
):
1523 if self
.user_response_cancel
:
1524 if isinstance(self
.user_response_cancel
, tuple):
1525 self
.user_response_cancel
[0](*self
.user_response_ok
[1:])
1527 self
.user_response_cancel()
1528 self
.call_cancel_on_destroy
= False
1531 class NonModalConfirmationDialog(HigDialog
):
1533 HIG compliant non modal confirmation dialog
1536 def __init__(self
, pritext
, sectext
='', on_response_ok
=None,
1537 on_response_cancel
=None):
1538 self
.user_response_ok
= on_response_ok
1539 self
.user_response_cancel
= on_response_cancel
1540 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1541 parent
= gajim
.interface
.roster
.window
1544 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1545 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1546 self
.on_response_cancel
)
1547 self
.set_modal(False)
1549 def on_response_ok(self
, widget
):
1550 if self
.user_response_ok
:
1551 if isinstance(self
.user_response_ok
, tuple):
1552 self
.user_response_ok
[0](*self
.user_response_ok
[1:])
1554 self
.user_response_ok()
1555 self
.call_cancel_on_destroy
= False
1558 def on_response_cancel(self
, widget
):
1559 if self
.user_response_cancel
:
1560 if isinstance(self
.user_response_cancel
, tuple):
1561 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1563 self
.user_response_cancel()
1564 self
.call_cancel_on_destroy
= False
1567 class WarningDialog(HigDialog
):
1569 HIG compliant warning dialog
1572 def __init__(self
, pritext
, sectext
='', transient_for
=None):
1573 if not transient_for
and hasattr(gajim
.interface
, 'roster') and \
1574 gajim
.interface
.roster
:
1575 transient_for
= gajim
.interface
.roster
.window
1576 HigDialog
.__init
__(self
, transient_for
, gtk
.MESSAGE_WARNING
,
1577 gtk
.BUTTONS_OK
, pritext
, sectext
)
1578 self
.set_modal(False)
1581 class InformationDialog(HigDialog
):
1583 HIG compliant info dialog
1586 def __init__(self
, pritext
, sectext
=''):
1587 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1588 parent
= gajim
.interface
.roster
.window
1591 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_INFO
, gtk
.BUTTONS_OK
,
1593 self
.set_modal(False)
1596 class ErrorDialog(HigDialog
):
1598 HIG compliant error dialog
1601 def __init__(self
, pritext
, sectext
='', on_response_ok
=None,
1602 on_response_cancel
=None):
1603 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1604 parent
= gajim
.interface
.roster
.window
1607 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
,
1608 pritext
, sectext
, on_response_ok
=on_response_ok
,
1609 on_response_cancel
=on_response_cancel
)
1612 class YesNoDialog(HigDialog
):
1614 HIG compliant YesNo dialog
1617 def __init__(self
, pritext
, sectext
='', checktext
='', on_response_yes
=None,
1618 on_response_no
=None):
1619 self
.user_response_yes
= on_response_yes
1620 self
.user_response_no
= on_response_no
1621 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1622 parent
= gajim
.interface
.roster
.window
1625 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1626 gtk
.BUTTONS_YES_NO
, pritext
, sectext
,
1627 on_response_yes
=self
.on_response_yes
,
1628 on_response_no
=self
.on_response_no
)
1631 self
.checkbutton
= gtk
.CheckButton(checktext
)
1632 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1634 self
.checkbutton
= None
1635 self
.set_modal(False)
1638 def on_response_yes(self
, widget
):
1639 if self
.user_response_yes
:
1640 if isinstance(self
.user_response_yes
, tuple):
1641 self
.user_response_yes
[0](self
.is_checked(),
1642 *self
.user_response_yes
[1:])
1644 self
.user_response_yes(self
.is_checked())
1645 self
.call_cancel_on_destroy
= False
1648 def on_response_no(self
, widget
):
1649 if self
.user_response_no
:
1650 if isinstance(self
.user_response_no
, tuple):
1651 self
.user_response_no
[0](*self
.user_response_no
[1:])
1653 self
.user_response_no()
1654 self
.call_cancel_on_destroy
= False
1657 def is_checked(self
):
1659 Get active state of the checkbutton
1661 if not self
.checkbutton
:
1663 return self
.checkbutton
.get_active()
1665 class ConfirmationDialogCheck(ConfirmationDialog
):
1667 HIG compliant confirmation dialog with checkbutton
1670 def __init__(self
, pritext
, sectext
='', checktext
='', on_response_ok
=None,
1671 on_response_cancel
=None, is_modal
=True):
1672 self
.user_response_ok
= on_response_ok
1673 self
.user_response_cancel
= on_response_cancel
1675 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1676 parent
= gajim
.interface
.roster
.window
1679 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1680 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1681 self
.on_response_cancel
)
1683 self
.set_default_response(gtk
.RESPONSE_OK
)
1685 ok_button
= self
.action_area
.get_children()[0] # right to left
1686 ok_button
.grab_focus()
1688 self
.checkbutton
= gtk
.CheckButton(checktext
)
1689 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1690 self
.set_modal(is_modal
)
1693 def on_response_ok(self
, widget
):
1694 if self
.user_response_ok
:
1695 if isinstance(self
.user_response_ok
, tuple):
1696 self
.user_response_ok
[0](self
.is_checked(),
1697 *self
.user_response_ok
[1:])
1699 self
.user_response_ok(self
.is_checked())
1700 self
.call_cancel_on_destroy
= False
1703 def on_response_cancel(self
, widget
):
1704 if self
.user_response_cancel
:
1705 if isinstance(self
.user_response_cancel
, tuple):
1706 self
.user_response_cancel
[0](self
.is_checked(),
1707 *self
.user_response_cancel
[1:])
1709 self
.user_response_cancel(self
.is_checked())
1710 self
.call_cancel_on_destroy
= False
1713 def is_checked(self
):
1715 Get active state of the checkbutton
1717 return self
.checkbutton
.get_active()
1719 class ConfirmationDialogDoubleCheck(ConfirmationDialog
):
1721 HIG compliant confirmation dialog with 2 checkbuttons
1724 def __init__(self
, pritext
, sectext
='', checktext1
='', checktext2
='',
1725 on_response_ok
=None, on_response_cancel
=None, is_modal
=True):
1726 self
.user_response_ok
= on_response_ok
1727 self
.user_response_cancel
= on_response_cancel
1729 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1730 parent
= gajim
.interface
.roster
.window
1733 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1734 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1735 self
.on_response_cancel
)
1737 self
.set_default_response(gtk
.RESPONSE_OK
)
1739 ok_button
= self
.action_area
.get_children()[0] # right to left
1740 ok_button
.grab_focus()
1743 self
.checkbutton1
= gtk
.CheckButton(checktext1
)
1744 self
.vbox
.pack_start(self
.checkbutton1
, expand
=False, fill
=True)
1746 self
.checkbutton1
= None
1748 self
.checkbutton2
= gtk
.CheckButton(checktext2
)
1749 self
.vbox
.pack_start(self
.checkbutton2
, expand
=False, fill
=True)
1751 self
.checkbutton2
= None
1753 self
.set_modal(is_modal
)
1756 def on_response_ok(self
, widget
):
1757 if self
.user_response_ok
:
1758 if isinstance(self
.user_response_ok
, tuple):
1759 self
.user_response_ok
[0](self
.is_checked(),
1760 *self
.user_response_ok
[1:])
1762 self
.user_response_ok(self
.is_checked())
1763 self
.call_cancel_on_destroy
= False
1766 def on_response_cancel(self
, widget
):
1767 if self
.user_response_cancel
:
1768 if isinstance(self
.user_response_cancel
, tuple):
1769 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1771 self
.user_response_cancel()
1772 self
.call_cancel_on_destroy
= False
1775 def is_checked(self
):
1776 ''' Get active state of the checkbutton '''
1777 if self
.checkbutton1
:
1778 is_checked_1
= self
.checkbutton1
.get_active()
1780 is_checked_1
= False
1781 if self
.checkbutton2
:
1782 is_checked_2
= self
.checkbutton2
.get_active()
1784 is_checked_2
= False
1785 return [is_checked_1
, is_checked_2
]
1787 class ConfirmationDialogDoubleRadio(ConfirmationDialog
):
1789 HIG compliant confirmation dialog with 2 radios
1792 def __init__(self
, pritext
, sectext
='', radiotext1
='', radiotext2
='',
1793 on_response_ok
=None, on_response_cancel
=None, is_modal
=True):
1794 self
.user_response_ok
= on_response_ok
1795 self
.user_response_cancel
= on_response_cancel
1797 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1798 parent
= gajim
.interface
.roster
.window
1801 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1802 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1803 self
.on_response_cancel
)
1805 self
.set_default_response(gtk
.RESPONSE_OK
)
1807 ok_button
= self
.action_area
.get_children()[0] # right to left
1808 ok_button
.grab_focus()
1810 self
.radiobutton1
= gtk
.RadioButton(label
=radiotext1
)
1811 self
.vbox
.pack_start(self
.radiobutton1
, expand
=False, fill
=True)
1813 self
.radiobutton2
= gtk
.RadioButton(group
=self
.radiobutton1
,
1815 self
.vbox
.pack_start(self
.radiobutton2
, expand
=False, fill
=True)
1817 self
.set_modal(is_modal
)
1820 def on_response_ok(self
, widget
):
1821 if self
.user_response_ok
:
1822 if isinstance(self
.user_response_ok
, tuple):
1823 self
.user_response_ok
[0](self
.is_checked(),
1824 *self
.user_response_ok
[1:])
1826 self
.user_response_ok(self
.is_checked())
1827 self
.call_cancel_on_destroy
= False
1830 def on_response_cancel(self
, widget
):
1831 if self
.user_response_cancel
:
1832 if isinstance(self
.user_response_cancel
, tuple):
1833 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1835 self
.user_response_cancel()
1836 self
.call_cancel_on_destroy
= False
1839 def is_checked(self
):
1840 ''' Get active state of the checkbutton '''
1841 if self
.radiobutton1
:
1842 is_checked_1
= self
.radiobutton1
.get_active()
1844 is_checked_1
= False
1845 if self
.radiobutton2
:
1846 is_checked_2
= self
.radiobutton2
.get_active()
1848 is_checked_2
= False
1849 return [is_checked_1
, is_checked_2
]
1851 class FTOverwriteConfirmationDialog(ConfirmationDialog
):
1853 HIG compliant confirmation dialog to overwrite or resume a file transfert
1856 def __init__(self
, pritext
, sectext
='', propose_resume
=True,
1858 if hasattr(gajim
.interface
, 'roster') and gajim
.interface
.roster
:
1859 parent
= gajim
.interface
.roster
.window
1862 HigDialog
.__init
__(self
, parent
, gtk
.MESSAGE_QUESTION
,
1863 gtk
.BUTTONS_CANCEL
, pritext
, sectext
)
1865 self
.on_response
= on_response
1868 b
= gtk
.Button('', gtk
.STOCK_REFRESH
)
1869 align
= b
.get_children()[0]
1870 hbox
= align
.get_children()[0]
1871 label
= hbox
.get_children()[1]
1872 label
.set_text(_('_Resume'))
1873 label
.set_use_underline(True)
1874 self
.add_action_widget(b
, 100)
1876 b
= gtk
.Button('', gtk
.STOCK_SAVE_AS
)
1877 align
= b
.get_children()[0]
1878 hbox
= align
.get_children()[0]
1879 label
= hbox
.get_children()[1]
1880 label
.set_text(_('Re_place'))
1881 label
.set_use_underline(True)
1882 self
.add_action_widget(b
, 200)
1884 self
.connect('response', self
.on_dialog_response
)
1887 def on_dialog_response(self
, dialog
, response
):
1888 if self
.on_response
:
1889 if isinstance(self
.on_response
, tuple):
1890 self
.on_response
[0](response
, *self
.on_response
[1:])
1892 self
.on_response(response
)
1893 self
.call_cancel_on_destroy
= False
1896 class CommonInputDialog
:
1898 Common Class for Input dialogs
1901 def __init__(self
, title
, label_str
, is_modal
, ok_handler
, cancel_handler
):
1902 self
.dialog
= self
.xml
.get_object('input_dialog')
1903 label
= self
.xml
.get_object('label')
1904 self
.dialog
.set_title(title
)
1905 label
.set_markup(label_str
)
1906 self
.cancel_handler
= cancel_handler
1907 self
.vbox
= self
.xml
.get_object('vbox')
1909 self
.ok_handler
= ok_handler
1910 okbutton
= self
.xml
.get_object('okbutton')
1911 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
1912 cancelbutton
= self
.xml
.get_object('cancelbutton')
1913 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
1914 self
.xml
.connect_signals(self
)
1915 self
.dialog
.show_all()
1917 def on_input_dialog_destroy(self
, widget
):
1918 if self
.cancel_handler
:
1919 self
.cancel_handler()
1921 def on_okbutton_clicked(self
, widget
):
1922 user_input
= self
.get_text()
1924 user_input
= user_input
.decode('utf-8')
1925 self
.cancel_handler
= None
1926 self
.dialog
.destroy()
1927 if isinstance(self
.ok_handler
, tuple):
1928 self
.ok_handler
[0](user_input
, *self
.ok_handler
[1:])
1930 self
.ok_handler(user_input
)
1932 def on_cancelbutton_clicked(self
, widget
):
1933 self
.dialog
.destroy()
1935 class InputDialog(CommonInputDialog
):
1937 Class for Input dialog
1940 def __init__(self
, title
, label_str
, input_str
=None, is_modal
=True,
1941 ok_handler
=None, cancel_handler
=None):
1942 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_dialog.ui')
1943 CommonInputDialog
.__init
__(self
, title
, label_str
, is_modal
, ok_handler
,
1945 self
.input_entry
= self
.xml
.get_object('input_entry')
1947 self
.set_entry(input_str
)
1949 def on_input_dialog_delete_event(self
, widget
, event
):
1951 may be implemented by subclasses
1955 def set_entry(self
, value
):
1956 self
.input_entry
.set_text(value
)
1957 self
.input_entry
.select_region(0, -1) # select all
1960 return self
.input_entry
.get_text().decode('utf-8')
1962 class InputDialogCheck(InputDialog
):
1964 Class for Input dialog
1967 def __init__(self
, title
, label_str
, checktext
='', input_str
=None,
1968 is_modal
=True, ok_handler
=None, cancel_handler
=None):
1969 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_dialog.ui')
1970 InputDialog
.__init
__(self
, title
, label_str
, input_str
=input_str
,
1971 is_modal
=is_modal
, ok_handler
=ok_handler
,
1972 cancel_handler
=cancel_handler
)
1973 self
.input_entry
= self
.xml
.get_object('input_entry')
1975 self
.input_entry
.set_text(input_str
)
1976 self
.input_entry
.select_region(0, -1) # select all
1979 self
.checkbutton
= gtk
.CheckButton(checktext
)
1980 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1981 self
.checkbutton
.show()
1983 def on_okbutton_clicked(self
, widget
):
1984 user_input
= self
.get_text()
1986 user_input
= user_input
.decode('utf-8')
1987 self
.cancel_handler
= None
1988 self
.dialog
.destroy()
1989 if isinstance(self
.ok_handler
, tuple):
1990 self
.ok_handler
[0](user_input
, self
.is_checked(), *self
.ok_handler
[1:])
1992 self
.ok_handler(user_input
, self
.is_checked())
1995 return self
.input_entry
.get_text().decode('utf-8')
1997 def is_checked(self
):
1999 Get active state of the checkbutton
2002 return self
.checkbutton
.get_active()
2004 # There is no checkbutton
2007 class ChangeNickDialog(InputDialogCheck
):
2009 Class for changing room nickname in case of conflict
2012 def __init__(self
, account
, room_jid
, title
, prompt
, check_text
=None,
2015 change_nick must be set to True when we are already occupant of the room
2016 and we are changing our nick
2018 InputDialogCheck
.__init
__(self
, title
, '', checktext
=check_text
,
2019 input_str
='', is_modal
=True, ok_handler
=None, cancel_handler
=None)
2020 self
.room_queue
= [(account
, room_jid
, prompt
, change_nick
)]
2023 def on_input_dialog_delete_event(self
, widget
, event
):
2024 self
.on_cancelbutton_clicked(widget
)
2027 def setup_dialog(self
):
2028 self
.gc_control
= gajim
.interface
.msg_win_mgr
.get_gc_control(
2029 self
.room_jid
, self
.account
)
2030 if not self
.gc_control
and \
2031 self
.room_jid
in gajim
.interface
.minimized_controls
[self
.account
]:
2033 gajim
.interface
.minimized_controls
[self
.account
][self
.room_jid
]
2034 if not self
.gc_control
:
2037 label
= self
.xml
.get_object('label')
2038 label
.set_markup(self
.prompt
)
2039 self
.set_entry(self
.gc_control
.nick
+ \
2040 gajim
.config
.get('gc_proposed_nick_char'))
2042 def check_next(self
):
2043 if len(self
.room_queue
) == 0:
2044 self
.cancel_handler
= None
2045 self
.dialog
.destroy()
2046 if 'change_nick_dialog' in gajim
.interface
.instances
:
2047 del gajim
.interface
.instances
['change_nick_dialog']
2049 self
.account
, self
.room_jid
, self
.prompt
, self
.change_nick
= \
2050 self
.room_queue
.pop(0)
2053 if gajim
.new_room_nick
is not None and not gajim
.gc_connected
[
2054 self
.account
][self
.room_jid
] and self
.gc_control
.nick
!= \
2055 gajim
.new_room_nick
:
2057 self
.on_ok(gajim
.new_room_nick
, True)
2061 def on_okbutton_clicked(self
, widget
):
2062 nick
= self
.get_text()
2064 nick
= nick
.decode('utf-8')
2065 # send presence to room
2067 nick
= helpers
.parse_resource(nick
)
2070 ErrorDialog(_('Invalid nickname'),
2071 _('The nickname has not allowed characters.'))
2073 self
.on_ok(nick
, self
.is_checked())
2075 def on_ok(self
, nick
, is_checked
):
2077 gajim
.new_room_nick
= nick
2078 gajim
.connections
[self
.account
].join_gc(nick
, self
.room_jid
, None,
2079 change_nick
=self
.change_nick
)
2080 if gajim
.gc_connected
[self
.account
][self
.room_jid
]:
2081 # We are changing nick, we will change self.nick when we receive
2082 # presence that inform that it works
2083 self
.gc_control
.new_nick
= nick
2085 # We are connecting, we will not get a changed nick presence so
2086 # change it NOW. We don't already have a nick so it's harmless
2087 self
.gc_control
.nick
= nick
2090 def on_cancelbutton_clicked(self
, widget
):
2091 self
.gc_control
.new_nick
= ''
2094 def add_room(self
, account
, room_jid
, prompt
, change_nick
=False):
2095 if (account
, room_jid
, prompt
, change_nick
) not in self
.room_queue
:
2096 self
.room_queue
.append((account
, room_jid
, prompt
, change_nick
))
2098 class InputTextDialog(CommonInputDialog
):
2100 Class for multilines Input dialog (more place than InputDialog)
2103 def __init__(self
, title
, label_str
, input_str
=None, is_modal
=True,
2104 ok_handler
=None, cancel_handler
=None):
2105 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_text_dialog.ui')
2106 CommonInputDialog
.__init
__(self
, title
, label_str
, is_modal
, ok_handler
,
2108 self
.input_buffer
= self
.xml
.get_object('input_textview').get_buffer()
2110 self
.input_buffer
.set_text(input_str
)
2111 start_iter
, end_iter
= self
.input_buffer
.get_bounds()
2112 self
.input_buffer
.select_range(start_iter
, end_iter
) # select all
2115 start_iter
, end_iter
= self
.input_buffer
.get_bounds()
2116 return self
.input_buffer
.get_text(start_iter
, end_iter
).decode('utf-8')
2118 class DoubleInputDialog
:
2120 Class for Double Input dialog
2123 def __init__(self
, title
, label_str1
, label_str2
, input_str1
=None,
2124 input_str2
=None, is_modal
=True, ok_handler
=None, cancel_handler
=None):
2125 self
.xml
= gtkgui_helpers
.get_gtk_builder('dubbleinput_dialog.ui')
2126 self
.dialog
= self
.xml
.get_object('dubbleinput_dialog')
2127 label1
= self
.xml
.get_object('label1')
2128 self
.input_entry1
= self
.xml
.get_object('input_entry1')
2129 label2
= self
.xml
.get_object('label2')
2130 self
.input_entry2
= self
.xml
.get_object('input_entry2')
2131 self
.dialog
.set_title(title
)
2132 label1
.set_markup(label_str1
)
2133 label2
.set_markup(label_str2
)
2134 self
.cancel_handler
= cancel_handler
2136 self
.input_entry1
.set_text(input_str1
)
2137 self
.input_entry1
.select_region(0, -1) # select all
2139 self
.input_entry2
.set_text(input_str2
)
2140 self
.input_entry2
.select_region(0, -1) # select all
2142 self
.dialog
.set_modal(is_modal
)
2144 self
.ok_handler
= ok_handler
2145 okbutton
= self
.xml
.get_object('okbutton')
2146 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
2147 cancelbutton
= self
.xml
.get_object('cancelbutton')
2148 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
2149 self
.xml
.connect_signals(self
)
2150 self
.dialog
.show_all()
2152 def on_dubbleinput_dialog_destroy(self
, widget
):
2153 if not self
.cancel_handler
:
2155 if isinstance(self
.cancel_handler
, tuple):
2156 self
.cancel_handler
[0](*self
.cancel_handler
[1:])
2158 self
.cancel_handler()
2160 def on_okbutton_clicked(self
, widget
):
2161 user_input1
= self
.input_entry1
.get_text().decode('utf-8')
2162 user_input2
= self
.input_entry2
.get_text().decode('utf-8')
2163 self
.dialog
.destroy()
2164 if not self
.ok_handler
:
2166 if isinstance(self
.ok_handler
, tuple):
2167 self
.ok_handler
[0](user_input1
, user_input2
, *self
.ok_handler
[1:])
2169 self
.ok_handler(user_input1
, user_input2
)
2171 def on_cancelbutton_clicked(self
, widget
):
2172 self
.dialog
.destroy()
2173 if not self
.cancel_handler
:
2175 if isinstance(self
.cancel_handler
, tuple):
2176 self
.cancel_handler
[0](*self
.cancel_handler
[1:])
2178 self
.cancel_handler()
2180 class SubscriptionRequestWindow
:
2181 def __init__(self
, jid
, text
, account
, user_nick
=None):
2182 xml
= gtkgui_helpers
.get_gtk_builder('subscription_request_window.ui')
2183 self
.window
= xml
.get_object('subscription_request_window')
2185 self
.account
= account
2186 self
.user_nick
= user_nick
2187 if len(gajim
.connections
) >= 2:
2189 _('Subscription request for account %(account)s from %(jid)s')\
2190 % {'account': account
, 'jid': self
.jid
}
2192 prompt_text
= _('Subscription request from %s') % self
.jid
2193 xml
.get_object('from_label').set_text(prompt_text
)
2194 xml
.get_object('message_textview').get_buffer().set_text(text
)
2195 xml
.connect_signals(self
)
2196 self
.window
.show_all()
2198 def on_subscription_request_window_destroy(self
, widget
):
2202 if self
.jid
in gajim
.interface
.instances
[self
.account
]['sub_request']:
2203 # remove us from open windows
2204 del gajim
.interface
.instances
[self
.account
]['sub_request'][self
.jid
]
2206 def prepare_popup_menu(self
):
2207 xml
= gtkgui_helpers
.get_gtk_builder('subscription_request_popup_menu.ui')
2208 menu
= xml
.get_object('subscription_request_popup_menu')
2209 xml
.connect_signals(self
)
2212 def on_close_button_clicked(self
, widget
):
2213 self
.window
.destroy()
2215 def on_authorize_button_clicked(self
, widget
):
2219 gajim
.connections
[self
.account
].send_authorization(self
.jid
)
2220 self
.window
.destroy()
2221 contact
= gajim
.contacts
.get_contact(self
.account
, self
.jid
)
2222 if not contact
or _('Not in Roster') in contact
.groups
:
2223 AddNewContactWindow(self
.account
, self
.jid
, self
.user_nick
)
2225 def on_contact_info_activate(self
, widget
):
2229 if self
.jid
in gajim
.interface
.instances
[self
.account
]['infos']:
2230 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
].window
.present()
2232 contact
= gajim
.contacts
.create_contact(jid
=self
.jid
, account
=self
.account
)
2233 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
] = \
2234 vcard
.VcardWindow(contact
, self
.account
)
2235 # Remove jabber page
2236 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
].xml
.\
2237 get_object('information_notebook').remove_page(0)
2239 def on_start_chat_activate(self
, widget
):
2243 gajim
.interface
.new_chat_from_jid(self
.account
, self
.jid
)
2245 def on_deny_button_clicked(self
, widget
):
2249 gajim
.connections
[self
.account
].refuse_authorization(self
.jid
)
2250 contact
= gajim
.contacts
.get_contact(self
.account
, self
.jid
)
2251 if contact
and _('Not in Roster') in contact
.get_shown_groups():
2252 gajim
.interface
.roster
.remove_contact(self
.jid
, self
.account
)
2253 self
.window
.destroy()
2255 def on_actions_button_clicked(self
, widget
):
2259 menu
= self
.prepare_popup_menu()
2261 gtkgui_helpers
.popup_emoticons_under_button(menu
, widget
,
2265 class JoinGroupchatWindow
:
2266 def __init__(self
, account
=None, room_jid
='', nick
='', password
='',
2269 Automatic is a dict like {'invities': []}. If automatic is not empty,
2270 this means room must be automaticaly configured and when done, invities
2271 must be automatically invited
2274 if room_jid
!= '' and room_jid
in gajim
.gc_connected
[account
] and\
2275 gajim
.gc_connected
[account
][room_jid
]:
2276 ErrorDialog(_('You are already in group chat %s') % room_jid
)
2277 raise GajimGeneralException
, 'You are already in this group chat'
2279 nick
= gajim
.nicks
[account
]
2280 if gajim
.connections
[account
].connected
< 2:
2281 ErrorDialog(_('You are not connected to the server'),
2282 _('You can not join a group chat unless you are connected.'))
2283 raise GajimGeneralException
, 'You must be connected to join a groupchat'
2285 self
.xml
= gtkgui_helpers
.get_gtk_builder('join_groupchat_window.ui')
2287 account_label
= self
.xml
.get_object('account_label')
2288 account_combobox
= self
.xml
.get_object('account_combobox')
2289 account_label
.set_no_show_all(False)
2290 account_combobox
.set_no_show_all(False)
2291 liststore
= gtk
.ListStore(str)
2292 account_combobox
.set_model(liststore
)
2293 cell
= gtk
.CellRendererText()
2294 account_combobox
.pack_start(cell
, True)
2295 account_combobox
.add_attribute(cell
, 'text', 0)
2296 account_combobox
.set_active(-1)
2298 # Add accounts, set current as active if it matches 'account'
2299 for acct
in [a
for a
in gajim
.connections
if \
2300 gajim
.account_is_connected(a
)]:
2301 if gajim
.connections
[acct
].is_zeroconf
:
2303 account_combobox
.append_text(acct
)
2304 if account
and account
== acct
:
2305 account_combobox
.set_active(liststore
.iter_n_children(None)-1)
2307 self
.account
= account
2308 self
.automatic
= automatic
2309 self
._empty
_required
_widgets
= []
2311 self
.window
= self
.xml
.get_object('join_groupchat_window')
2312 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
2313 self
._room
_jid
_entry
= self
.xml
.get_object('room_jid_entry')
2314 self
._nickname
_entry
= self
.xml
.get_object('nickname_entry')
2315 self
._password
_entry
= self
.xml
.get_object('password_entry')
2317 self
._nickname
_entry
.set_text(nick
)
2319 self
._password
_entry
.set_text(password
)
2320 self
.xml
.connect_signals(self
)
2323 # now add us to open windows
2324 gajim
.interface
.instances
[account
]['join_gc'] = self
2325 if len(gajim
.connections
) > 1:
2326 title
= _('Join Group Chat with account %s') % account
2328 title
= _('Join Group Chat')
2329 self
.window
.set_title(title
)
2331 self
.server_comboboxentry
= self
.xml
.get_object('server_comboboxentry')
2332 self
.server_model
= self
.server_comboboxentry
.get_model()
2334 # get the muc server of our server
2335 if 'jabber' in gajim
.connections
[account
].muc_jid
:
2336 server_list
.append(gajim
.connections
[account
].muc_jid
['jabber'])
2338 self
.recently_combobox
= self
.xml
.get_object('recently_combobox')
2339 liststore
= gtk
.ListStore(str)
2340 self
.recently_combobox
.set_model(liststore
)
2341 cell
= gtk
.CellRendererText()
2342 self
.recently_combobox
.pack_start(cell
, True)
2343 self
.recently_combobox
.add_attribute(cell
, 'text', 0)
2344 self
.recently_groupchat
= gajim
.config
.get('recently_groupchat').split()
2345 for g
in self
.recently_groupchat
:
2346 self
.recently_combobox
.append_text(g
)
2347 server
= gajim
.get_server_from_jid(g
)
2348 if server
not in server_list
and not server
.startswith('irc'):
2349 server_list
.append(server
)
2351 for s
in server_list
:
2352 self
.server_model
.append([s
])
2353 self
.server_comboboxentry
.set_active(0)
2355 self
._set
_room
_jid
(room_jid
)
2357 if len(self
.recently_groupchat
) == 0:
2358 self
.recently_combobox
.set_sensitive(False)
2359 elif room_jid
== '':
2360 self
.recently_combobox
.set_active(0)
2361 self
._room
_jid
_entry
.select_region(0, -1)
2362 elif room_jid
!= '':
2363 self
.xml
.get_object('join_button').grab_focus()
2365 if not self
._room
_jid
_entry
.get_text():
2366 self
._empty
_required
_widgets
.append(self
._room
_jid
_entry
)
2367 if not self
._nickname
_entry
.get_text():
2368 self
._empty
_required
_widgets
.append(self
._nickname
_entry
)
2369 if len(self
._empty
_required
_widgets
):
2370 self
.xml
.get_object('join_button').set_sensitive(False)
2372 if account
and not gajim
.connections
[account
].private_storage_supported
:
2373 self
.xml
.get_object('bookmark_checkbutton').set_sensitive(False)
2375 self
.window
.show_all()
2377 def on_join_groupchat_window_destroy(self
, widget
):
2381 if self
.account
and 'join_gc' in gajim
.interface
.instances
[self
.account
]:
2382 # remove us from open windows
2383 del gajim
.interface
.instances
[self
.account
]['join_gc']
2385 def on_join_groupchat_window_key_press_event(self
, widget
, event
):
2386 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
2389 def on_required_entry_changed(self
, widget
):
2390 if not widget
.get_text():
2391 self
._empty
_required
_widgets
.append(widget
)
2392 self
.xml
.get_object('join_button').set_sensitive(False)
2394 if widget
in self
._empty
_required
_widgets
:
2395 self
._empty
_required
_widgets
.remove(widget
)
2396 if not self
._empty
_required
_widgets
and self
.account
:
2397 self
.xml
.get_object('join_button').set_sensitive(True)
2398 text
= self
._room
_jid
_entry
.get_text()
2399 if widget
== self
._room
_jid
_entry
and '@' in text
:
2400 # Don't allow @ char in room entry
2401 room_jid
, server
= text
.split('@', 1)
2402 self
._room
_jid
_entry
.set_text(room_jid
)
2404 self
.server_comboboxentry
.child
.set_text(server
)
2405 self
.server_comboboxentry
.grab_focus()
2407 def on_account_combobox_changed(self
, widget
):
2408 model
= widget
.get_model()
2409 iter_
= widget
.get_active_iter()
2410 self
.account
= model
[iter_
][0].decode('utf-8')
2411 self
.on_required_entry_changed(self
._nickname
_entry
)
2413 def _set_room_jid(self
, room_jid
):
2414 room
, server
= gajim
.get_name_and_server_from_jid(room_jid
)
2415 self
._room
_jid
_entry
.set_text(room
)
2416 self
.server_comboboxentry
.child
.set_text(server
)
2418 def on_recently_combobox_changed(self
, widget
):
2419 model
= widget
.get_model()
2420 iter_
= widget
.get_active_iter()
2421 room_jid
= model
[iter_
][0].decode('utf-8')
2422 self
._set
_room
_jid
(room_jid
)
2424 def on_browse_rooms_button_clicked(self
, widget
):
2425 server
= self
.server_comboboxentry
.child
.get_text().decode('utf-8')
2426 if server
in gajim
.interface
.instances
[self
.account
]['disco']:
2427 gajim
.interface
.instances
[self
.account
]['disco'][server
].window
.\
2431 # Object will add itself to the window dict
2433 disco
.ServiceDiscoveryWindow(self
.account
, server
,
2434 initial_identities
=[{'category': 'conference',
2436 except GajimGeneralException
:
2439 def on_cancel_button_clicked(self
, widget
):
2441 When Cancel button is clicked
2443 self
.window
.destroy()
2445 def on_bookmark_checkbutton_toggled(self
, widget
):
2446 auto_join_checkbutton
= self
.xml
.get_object('auto_join_checkbutton')
2447 if widget
.get_active():
2448 auto_join_checkbutton
.set_sensitive(True)
2450 auto_join_checkbutton
.set_sensitive(False)
2452 def on_join_button_clicked(self
, widget
):
2454 When Join button is clicked
2456 if not self
.account
:
2457 ErrorDialog(_('Invalid Account'),
2458 _('You have to choose an account from which you want to join the '
2461 nickname
= self
._nickname
_entry
.get_text().decode('utf-8')
2462 server
= self
.server_comboboxentry
.child
.get_text().decode('utf-8')
2463 room
= self
._room
_jid
_entry
.get_text().decode('utf-8')
2464 room_jid
= room
+ '@' + server
2465 password
= self
._password
_entry
.get_text().decode('utf-8')
2467 nickname
= helpers
.parse_resource(nickname
)
2469 ErrorDialog(_('Invalid Nickname'),
2470 _('The nickname has not allowed characters.'))
2472 user
, server
, resource
= helpers
.decompose_jid(room_jid
)
2473 if not user
or not server
or resource
:
2474 ErrorDialog(_('Invalid group chat Jabber ID'),
2475 _('Please enter the group chat Jabber ID as room@server.'))
2478 room_jid
= helpers
.parse_jid(room_jid
)
2480 ErrorDialog(_('Invalid group chat Jabber ID'),
2481 _('The group chat Jabber ID has not allowed characters.'))
2484 if gajim
.contacts
.get_contact(self
.account
, room_jid
):
2485 ErrorDialog(_('This is not a group chat'),
2486 _('%s is not the name of a group chat.') % room_jid
)
2488 if room_jid
in self
.recently_groupchat
:
2489 self
.recently_groupchat
.remove(room_jid
)
2490 self
.recently_groupchat
.insert(0, room_jid
)
2491 if len(self
.recently_groupchat
) > 10:
2492 self
.recently_groupchat
= self
.recently_groupchat
[0:10]
2493 gajim
.config
.set('recently_groupchat',
2494 ' '.join(self
.recently_groupchat
))
2496 if self
.xml
.get_object('bookmark_checkbutton').get_active():
2497 if self
.xml
.get_object('auto_join_checkbutton').get_active():
2501 # Add as bookmark, with autojoin and not minimized
2502 name
= gajim
.get_nick_from_jid(room_jid
)
2503 gajim
.interface
.add_gc_bookmark(self
.account
, name
, room_jid
, autojoin
,
2504 '0', password
, nickname
)
2507 gajim
.automatic_rooms
[self
.account
][room_jid
] = self
.automatic
2509 gajim
.interface
.join_gc_room(self
.account
, room_jid
, nickname
, password
)
2511 self
.window
.destroy()
2513 class SynchroniseSelectAccountDialog
:
2514 def __init__(self
, account
):
2515 # 'account' can be None if we are about to create our first one
2516 if not account
or gajim
.connections
[account
].connected
< 2:
2517 ErrorDialog(_('You are not connected to the server'),
2518 _('Without a connection, you can not synchronise your contacts.'))
2519 raise GajimGeneralException
, 'You are not connected to the server'
2520 self
.account
= account
2521 self
.xml
= gtkgui_helpers
.get_gtk_builder('synchronise_select_account_dialog.ui')
2522 self
.dialog
= self
.xml
.get_object('synchronise_select_account_dialog')
2523 self
.accounts_treeview
= self
.xml
.get_object('accounts_treeview')
2524 model
= gtk
.ListStore(str, str, bool)
2525 self
.accounts_treeview
.set_model(model
)
2527 renderer
= gtk
.CellRendererText()
2528 self
.accounts_treeview
.insert_column_with_attributes(-1, _('Name'),
2530 renderer
= gtk
.CellRendererText()
2531 self
.accounts_treeview
.insert_column_with_attributes(-1, _('Server'),
2534 self
.xml
.connect_signals(self
)
2535 self
.init_accounts()
2536 self
.dialog
.show_all()
2538 def on_accounts_window_key_press_event(self
, widget
, event
):
2539 if event
.keyval
== gtk
.keysyms
.Escape
:
2540 self
.window
.destroy()
2542 def init_accounts(self
):
2544 Initialize listStore with existing accounts
2546 model
= self
.accounts_treeview
.get_model()
2548 for remote_account
in gajim
.connections
:
2549 if remote_account
== self
.account
:
2550 # Do not show the account we're sync'ing
2552 iter_
= model
.append()
2553 model
.set(iter_
, 0, remote_account
, 1,
2554 gajim
.get_hostname_from_account(remote_account
))
2556 def on_cancel_button_clicked(self
, widget
):
2557 self
.dialog
.destroy()
2559 def on_ok_button_clicked(self
, widget
):
2560 sel
= self
.accounts_treeview
.get_selection()
2561 (model
, iter_
) = sel
.get_selected()
2564 remote_account
= model
.get_value(iter_
, 0).decode('utf-8')
2566 if gajim
.connections
[remote_account
].connected
< 2:
2567 ErrorDialog(_('This account is not connected to the server'),
2568 _('You cannot synchronize with an account unless it is connected.'))
2572 SynchroniseSelectContactsDialog(self
.account
, remote_account
)
2573 except GajimGeneralException
:
2574 # if we showed ErrorDialog, there will not be dialog instance
2576 self
.dialog
.destroy()
2578 class SynchroniseSelectContactsDialog
:
2579 def __init__(self
, account
, remote_account
):
2580 self
.local_account
= account
2581 self
.remote_account
= remote_account
2582 self
.xml
= gtkgui_helpers
.get_gtk_builder(
2583 'synchronise_select_contacts_dialog.ui')
2584 self
.dialog
= self
.xml
.get_object('synchronise_select_contacts_dialog')
2585 self
.contacts_treeview
= self
.xml
.get_object('contacts_treeview')
2586 model
= gtk
.ListStore(bool, str)
2587 self
.contacts_treeview
.set_model(model
)
2589 renderer1
= gtk
.CellRendererToggle()
2590 renderer1
.set_property('activatable', True)
2591 renderer1
.connect('toggled', self
.toggled_callback
)
2592 self
.contacts_treeview
.insert_column_with_attributes(-1,
2593 _('Synchronise'), renderer1
, active
=0)
2594 renderer2
= gtk
.CellRendererText()
2595 self
.contacts_treeview
.insert_column_with_attributes(-1, _('Name'),
2598 self
.xml
.connect_signals(self
)
2599 self
.init_contacts()
2600 self
.dialog
.show_all()
2602 def toggled_callback(self
, cell
, path
):
2603 model
= self
.contacts_treeview
.get_model()
2604 iter_
= model
.get_iter(path
)
2605 model
[iter_
][0] = not cell
.get_active()
2607 def on_contacts_window_key_press_event(self
, widget
, event
):
2608 if event
.keyval
== gtk
.keysyms
.Escape
:
2609 self
.window
.destroy()
2611 def init_contacts(self
):
2613 Initialize listStore with existing accounts
2615 model
= self
.contacts_treeview
.get_model()
2618 # recover local contacts
2619 local_jid_list
= gajim
.contacts
.get_contacts_jid_list(self
.local_account
)
2621 remote_jid_list
= gajim
.contacts
.get_contacts_jid_list(
2622 self
.remote_account
)
2623 for remote_jid
in remote_jid_list
:
2624 if remote_jid
not in local_jid_list
:
2625 iter_
= model
.append()
2626 model
.set(iter_
, 0, True, 1, remote_jid
)
2628 def on_cancel_button_clicked(self
, widget
):
2629 self
.dialog
.destroy()
2631 def on_ok_button_clicked(self
, widget
):
2632 model
= self
.contacts_treeview
.get_model()
2633 iter_
= model
.get_iter_root()
2637 remote_jid
= model
[iter_
][1].decode('utf-8')
2638 message
= 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
2639 gajim
.get_hostname_from_account(self
.remote_account
)
2640 remote_contact
= gajim
.contacts
.get_first_contact_from_jid(
2641 self
.remote_account
, remote_jid
)
2642 # keep same groups and same nickname
2643 gajim
.interface
.roster
.req_sub(self
, remote_jid
, message
,
2644 self
.local_account
, groups
= remote_contact
.groups
,
2645 nickname
= remote_contact
.name
, auto_auth
= True)
2646 iter_
= model
.iter_next(iter_
)
2647 self
.dialog
.destroy()
2649 class NewChatDialog(InputDialog
):
2650 def __init__(self
, account
):
2651 self
.account
= account
2653 if len(gajim
.connections
) > 1:
2654 title
= _('Start Chat with account %s') % account
2656 title
= _('Start Chat')
2657 prompt_text
= _('Fill in the nickname or the Jabber ID of the contact you would like\nto send a chat message to:')
2658 InputDialog
.__init
__(self
, title
, prompt_text
, is_modal
=False)
2660 self
.completion_dict
= {}
2661 liststore
= gtkgui_helpers
.get_completion_liststore(self
.input_entry
)
2662 self
.completion_dict
= helpers
.get_contact_dict_for_account(account
)
2663 # add all contacts to the model
2664 keys
= sorted(self
.completion_dict
.keys())
2666 contact
= self
.completion_dict
[jid
]
2667 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
2668 liststore
.append((img
.get_pixbuf(), jid
))
2670 self
.ok_handler
= self
.new_chat_response
2671 okbutton
= self
.xml
.get_object('okbutton')
2672 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
2673 cancelbutton
= self
.xml
.get_object('cancelbutton')
2674 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
2675 self
.dialog
.set_transient_for(gajim
.interface
.roster
.window
)
2676 self
.dialog
.show_all()
2678 def new_chat_response(self
, jid
):
2680 Called when ok button is clicked
2682 if gajim
.connections
[self
.account
].connected
<= 1:
2683 #if offline or connecting
2684 ErrorDialog(_('Connection not available'),
2685 _('Please make sure you are connected with "%s".') % self
.account
)
2688 if jid
in self
.completion_dict
:
2689 jid
= self
.completion_dict
[jid
].jid
2692 jid
= helpers
.parse_jid(jid
)
2693 except helpers
.InvalidFormat
, e
:
2694 ErrorDialog(_('Invalid JID'), e
[0])
2697 ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".') % jid
)
2699 gajim
.interface
.new_chat_from_jid(self
.account
, jid
)
2701 class ChangePasswordDialog
:
2702 def __init__(self
, account
, on_response
):
2703 # 'account' can be None if we are about to create our first one
2704 if not account
or gajim
.connections
[account
].connected
< 2:
2705 ErrorDialog(_('You are not connected to the server'),
2706 _('Without a connection, you can not change your password.'))
2707 raise GajimGeneralException
, 'You are not connected to the server'
2708 self
.account
= account
2709 self
.on_response
= on_response
2710 self
.xml
= gtkgui_helpers
.get_gtk_builder('change_password_dialog.ui')
2711 self
.dialog
= self
.xml
.get_object('change_password_dialog')
2712 self
.password1_entry
= self
.xml
.get_object('password1_entry')
2713 self
.password2_entry
= self
.xml
.get_object('password2_entry')
2714 self
.dialog
.connect('response', self
.on_dialog_response
)
2716 self
.dialog
.show_all()
2718 def on_dialog_response(self
, dialog
, response
):
2719 if response
!= gtk
.RESPONSE_OK
:
2721 self
.on_response(None)
2723 password1
= self
.password1_entry
.get_text().decode('utf-8')
2725 ErrorDialog(_('Invalid password'), _('You must enter a password.'))
2727 password2
= self
.password2_entry
.get_text().decode('utf-8')
2728 if password1
!= password2
:
2729 ErrorDialog(_('Passwords do not match'),
2730 _('The passwords typed in both fields must be identical.'))
2733 self
.on_response(password1
)
2735 class PopupNotificationWindow
:
2736 def __init__(self
, event_type
, jid
, account
, msg_type
='',
2737 path_to_image
=None, title
=None, text
=None):
2738 self
.account
= account
2740 self
.msg_type
= msg_type
2742 xml
= gtkgui_helpers
.get_gtk_builder('popup_notification_window.ui')
2743 self
.window
= xml
.get_object('popup_notification_window')
2744 self
.window
.set_type_hint(gtk
.gdk
.WINDOW_TYPE_HINT_TOOLTIP
)
2745 close_button
= xml
.get_object('close_button')
2746 event_type_label
= xml
.get_object('event_type_label')
2747 event_description_label
= xml
.get_object('event_description_label')
2748 eventbox
= xml
.get_object('eventbox')
2749 image
= xml
.get_object('notification_image')
2752 text
= gajim
.get_name_from_jid(account
, jid
) # default value of text
2756 event_type_label
.set_markup(
2757 '<span foreground="black" weight="bold">%s</span>' %
2758 gobject
.markup_escape_text(title
))
2760 # set colors [ http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html ]
2761 self
.window
.modify_bg(gtk
.STATE_NORMAL
, gtk
.gdk
.color_parse('black'))
2764 if not path_to_image
:
2765 path_to_image
= gtkgui_helpers
.get_icon_path('gajim-chat_msg_recv', 48)
2767 if event_type
== _('Contact Signed In'):
2768 bg_color
= 'limegreen'
2769 elif event_type
== _('Contact Signed Out'):
2771 elif event_type
in (_('New Message'), _('New Single Message'),
2772 _('New Private Message'), _('New E-mail')):
2773 bg_color
= 'dodgerblue'
2774 elif event_type
== _('File Transfer Request'):
2776 elif event_type
== _('File Transfer Error'):
2777 bg_color
= 'firebrick'
2778 elif event_type
in (_('File Transfer Completed'),
2779 _('File Transfer Stopped')):
2780 bg_color
= 'yellowgreen'
2781 elif event_type
== _('Groupchat Invitation'):
2783 elif event_type
== _('Contact Changed Status'):
2784 bg_color
= 'thistle2'
2785 else: # Unknown event! Shouldn't happen but deal with it
2787 popup_bg_color
= gtk
.gdk
.color_parse(bg_color
)
2788 close_button
.modify_bg(gtk
.STATE_NORMAL
, popup_bg_color
)
2789 eventbox
.modify_bg(gtk
.STATE_NORMAL
, popup_bg_color
)
2790 event_description_label
.set_markup('<span foreground="black">%s</span>' %
2791 gobject
.markup_escape_text(text
))
2794 image
.set_from_file(path_to_image
)
2796 # position the window to bottom-right of screen
2797 window_width
, self
.window_height
= self
.window
.get_size()
2798 gajim
.interface
.roster
.popups_notification_height
+= self
.window_height
2799 pos_x
= gajim
.config
.get('notification_position_x')
2801 pos_x
= gtk
.gdk
.screen_width() - window_width
+ pos_x
+ 1
2802 pos_y
= gajim
.config
.get('notification_position_y')
2804 pos_y
= gtk
.gdk
.screen_height() - \
2805 gajim
.interface
.roster
.popups_notification_height
+ pos_y
+ 1
2806 self
.window
.move(pos_x
, pos_y
)
2808 xml
.connect_signals(self
)
2809 self
.window
.show_all()
2810 timeout
= gajim
.config
.get('notification_timeout')
2811 gobject
.timeout_add_seconds(timeout
, self
.on_timeout
)
2813 def on_close_button_clicked(self
, widget
):
2814 self
.adjust_height_and_move_popup_notification_windows()
2816 def on_timeout(self
):
2817 self
.adjust_height_and_move_popup_notification_windows()
2819 def adjust_height_and_move_popup_notification_windows(self
):
2821 gajim
.interface
.roster
.popups_notification_height
-= self
.window_height
2822 self
.window
.destroy()
2824 if len(gajim
.interface
.roster
.popup_notification_windows
) > 0:
2825 # we want to remove the first window added in the list
2826 gajim
.interface
.roster
.popup_notification_windows
.pop(0)
2828 # move the rest of popup windows
2829 gajim
.interface
.roster
.popups_notification_height
= 0
2830 for window_instance
in gajim
.interface
.roster
.popup_notification_windows
:
2831 window_width
, window_height
= window_instance
.window
.get_size()
2832 gajim
.interface
.roster
.popups_notification_height
+= window_height
2833 window_instance
.window
.move(gtk
.gdk
.screen_width() - window_width
,
2834 gtk
.gdk
.screen_height() - \
2835 gajim
.interface
.roster
.popups_notification_height
)
2837 def on_popup_notification_window_button_press_event(self
, widget
, event
):
2838 if event
.button
!= 1:
2839 self
.window
.destroy()
2841 gajim
.interface
.handle_event(self
.account
, self
.jid
, self
.msg_type
)
2842 self
.adjust_height_and_move_popup_notification_windows()
2844 class SingleMessageWindow
:
2846 SingleMessageWindow can send or show a received singled message depending on
2847 action argument which can be 'send' or 'receive'
2849 # Keep a reference on windows so garbage collector don't restroy them
2851 def __init__(self
, account
, to
='', action
='', from_whom
='', subject
='',
2852 message
='', resource
='', session
=None, form_node
=None):
2853 self
.instances
.append(self
)
2854 self
.account
= account
2855 self
.action
= action
2857 self
.subject
= subject
2858 self
.message
= message
2860 self
.from_whom
= from_whom
2861 self
.resource
= resource
2862 self
.session
= session
2864 self
.xml
= gtkgui_helpers
.get_gtk_builder('single_message_window.ui')
2865 self
.window
= self
.xml
.get_object('single_message_window')
2866 self
.count_chars_label
= self
.xml
.get_object('count_chars_label')
2867 self
.from_label
= self
.xml
.get_object('from_label')
2868 self
.from_entry
= self
.xml
.get_object('from_entry')
2869 self
.to_label
= self
.xml
.get_object('to_label')
2870 self
.to_entry
= self
.xml
.get_object('to_entry')
2871 self
.subject_entry
= self
.xml
.get_object('subject_entry')
2872 self
.message_scrolledwindow
= self
.xml
.get_object(
2873 'message_scrolledwindow')
2874 self
.message_textview
= self
.xml
.get_object('message_textview')
2875 self
.message_tv_buffer
= self
.message_textview
.get_buffer()
2876 self
.conversation_scrolledwindow
= self
.xml
.get_object(
2877 'conversation_scrolledwindow')
2878 self
.conversation_textview
= conversation_textview
.ConversationTextview(
2880 self
.conversation_textview
.tv
.show()
2881 self
.conversation_tv_buffer
= self
.conversation_textview
.tv
.get_buffer()
2882 self
.xml
.get_object('conversation_scrolledwindow').add(
2883 self
.conversation_textview
.tv
)
2885 self
.form_widget
= None
2886 parent_box
= self
.xml
.get_object('conversation_scrolledwindow').\
2889 dataform
= dataforms
.ExtendForm(node
=form_node
)
2890 self
.form_widget
= dataforms_widget
.DataFormWidget(dataform
)
2891 self
.form_widget
.show_all()
2892 parent_box
.add(self
.form_widget
)
2893 parent_box
.child_set_property(self
.form_widget
, 'position',
2894 parent_box
.child_get_property(self
.xml
.get_object(
2895 'conversation_scrolledwindow'), 'position'))
2896 self
.action
= 'form'
2898 self
.send_button
= self
.xml
.get_object('send_button')
2899 self
.reply_button
= self
.xml
.get_object('reply_button')
2900 self
.send_and_close_button
= self
.xml
.get_object('send_and_close_button')
2901 self
.cancel_button
= self
.xml
.get_object('cancel_button')
2902 self
.close_button
= self
.xml
.get_object('close_button')
2903 self
.message_tv_buffer
.connect('changed', self
.update_char_counter
)
2904 if isinstance(to
, list):
2905 jid
= ', '.join( [i
[0].get_full_jid() for i
in to
])
2906 self
.to_entry
.set_text(jid
)
2907 self
.to_entry
.set_sensitive(False)
2909 self
.to_entry
.set_text(to
)
2911 if gajim
.config
.get('use_speller') and HAS_GTK_SPELL
and action
== 'send':
2913 lang
= gajim
.config
.get('speller_language')
2916 gtkspell
.Spell(self
.conversation_textview
.tv
, lang
)
2917 gtkspell
.Spell(self
.message_textview
, lang
)
2918 except (gobject
.GError
, TypeError, RuntimeError, OSError):
2919 AspellDictError(lang
)
2921 self
.prepare_widgets_for(self
.action
)
2923 # set_text(None) raises TypeError exception
2924 if self
.subject
is None:
2926 self
.subject_entry
.set_text(self
.subject
)
2930 liststore
= gtkgui_helpers
.get_completion_liststore(self
.to_entry
)
2931 self
.completion_dict
= helpers
.get_contact_dict_for_account(account
)
2932 keys
= sorted(self
.completion_dict
.keys())
2934 contact
= self
.completion_dict
[jid
]
2935 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
2936 liststore
.append((img
.get_pixbuf(), jid
))
2938 self
.completion_dict
= {}
2939 self
.xml
.connect_signals(self
)
2941 # get window position and size from config
2942 gtkgui_helpers
.resize_window(self
.window
,
2943 gajim
.config
.get('single-msg-width'),
2944 gajim
.config
.get('single-msg-height'))
2945 gtkgui_helpers
.move_window(self
.window
,
2946 gajim
.config
.get('single-msg-x-position'),
2947 gajim
.config
.get('single-msg-y-position'))
2949 self
.window
.show_all()
2951 def on_single_message_window_destroy(self
, widget
):
2952 self
.instances
.remove(self
)
2953 c
= gajim
.contacts
.get_contact_with_highest_priority(self
.account
,
2956 # Groupchat is maybe already destroyed
2958 if c
.is_groupchat() and not self
.from_whom
in \
2959 gajim
.interface
.minimized_controls
[self
.account
] and self
.action
== \
2960 'receive' and gajim
.events
.get_nb_roster_events(self
.account
,
2961 self
.from_whom
, types
=['chat', 'normal']) == 0:
2962 gajim
.interface
.roster
.remove_groupchat(self
.from_whom
, self
.account
)
2964 def set_cursor_to_end(self
):
2965 end_iter
= self
.message_tv_buffer
.get_end_iter()
2966 self
.message_tv_buffer
.place_cursor(end_iter
)
2969 # save the window size and position
2970 x
, y
= self
.window
.get_position()
2971 gajim
.config
.set('single-msg-x-position', x
)
2972 gajim
.config
.set('single-msg-y-position', y
)
2973 width
, height
= self
.window
.get_size()
2974 gajim
.config
.set('single-msg-width', width
)
2975 gajim
.config
.set('single-msg-height', height
)
2976 gajim
.interface
.save_config()
2978 def on_single_message_window_delete_event(self
, window
, ev
):
2981 def prepare_widgets_for(self
, action
):
2982 if len(gajim
.connections
) > 1:
2983 if action
== 'send':
2984 title
= _('Single Message using account %s') % self
.account
2986 title
= _('Single Message in account %s') % self
.account
2988 title
= _('Single Message')
2990 if action
== 'send': # prepare UI for Sending
2991 title
= _('Send %s') % title
2992 self
.send_button
.show()
2993 self
.send_and_close_button
.show()
2994 self
.to_label
.show()
2995 self
.to_entry
.show()
2996 self
.reply_button
.hide()
2997 self
.from_label
.hide()
2998 self
.from_entry
.hide()
2999 self
.conversation_scrolledwindow
.hide()
3000 self
.message_scrolledwindow
.show()
3002 if self
.message
: # we come from a reply?
3003 self
.message_textview
.grab_focus()
3004 self
.cancel_button
.hide()
3005 self
.close_button
.show()
3006 self
.message_tv_buffer
.set_text(self
.message
)
3007 gobject
.idle_add(self
.set_cursor_to_end
)
3008 else: # we write a new message (not from reply)
3009 self
.close_button
.hide()
3010 if self
.to
: # do we already have jid?
3011 self
.subject_entry
.grab_focus()
3013 elif action
== 'receive': # prepare UI for Receiving
3014 title
= _('Received %s') % title
3015 self
.reply_button
.show()
3016 self
.from_label
.show()
3017 self
.from_entry
.show()
3018 self
.send_button
.hide()
3019 self
.send_and_close_button
.hide()
3020 self
.to_label
.hide()
3021 self
.to_entry
.hide()
3022 self
.conversation_scrolledwindow
.show()
3023 self
.message_scrolledwindow
.hide()
3026 self
.conversation_textview
.print_real_text(self
.message
)
3027 fjid
= self
.from_whom
3029 fjid
+= '/' + self
.resource
# Full jid of sender (with resource)
3030 self
.from_entry
.set_text(fjid
)
3031 self
.from_entry
.set_property('editable', False)
3032 self
.subject_entry
.set_property('editable', False)
3033 self
.reply_button
.grab_focus()
3034 self
.cancel_button
.hide()
3035 self
.close_button
.show()
3036 elif action
== 'form': # prepare UI for Receiving
3037 title
= _('Form %s') % title
3038 self
.send_button
.show()
3039 self
.send_and_close_button
.show()
3040 self
.to_label
.show()
3041 self
.to_entry
.show()
3042 self
.reply_button
.hide()
3043 self
.from_label
.hide()
3044 self
.from_entry
.hide()
3045 self
.conversation_scrolledwindow
.hide()
3046 self
.message_scrolledwindow
.hide()
3048 self
.window
.set_title(title
)
3050 def on_cancel_button_clicked(self
, widget
):
3052 self
.window
.destroy()
3054 def on_close_button_clicked(self
, widget
):
3056 self
.window
.destroy()
3058 def update_char_counter(self
, widget
):
3059 characters_no
= self
.message_tv_buffer
.get_char_count()
3060 self
.count_chars_label
.set_text(unicode(characters_no
))
3062 def send_single_message(self
):
3063 if gajim
.connections
[self
.account
].connected
<= 1:
3064 # if offline or connecting
3065 ErrorDialog(_('Connection not available'),
3066 _('Please make sure you are connected with "%s".') % self
.account
)
3068 if isinstance(self
.to
, list):
3069 sender_list
= [i
[0].jid
+ '/' + i
[0].resource
for i
in self
.to
]
3071 sender_list
= [self
.to_entry
.get_text().decode('utf-8')]
3073 for to_whom_jid
in sender_list
:
3074 if to_whom_jid
in self
.completion_dict
:
3075 to_whom_jid
= self
.completion_dict
[to_whom_jid
].jid
3077 to_whom_jid
= helpers
.parse_jid(to_whom_jid
)
3078 except helpers
.InvalidFormat
:
3079 ErrorDialog(_('Invalid Jabber ID'),
3080 _('It is not possible to send a message to %s, this JID is not '
3081 'valid.') % to_whom_jid
)
3084 subject
= self
.subject_entry
.get_text().decode('utf-8')
3085 begin
, end
= self
.message_tv_buffer
.get_bounds()
3086 message
= self
.message_tv_buffer
.get_text(begin
, end
).decode('utf-8')
3088 if '/announce/' in to_whom_jid
:
3089 gajim
.connections
[self
.account
].send_motd(to_whom_jid
, subject
,
3094 session
= self
.session
3096 session
= gajim
.connections
[self
.account
].make_new_session(
3099 if self
.form_widget
:
3100 form_node
= self
.form_widget
.data_form
3103 # FIXME: allow GPG message some day
3104 gajim
.connections
[self
.account
].send_message(to_whom_jid
, message
,
3105 keyID
=None, type_
='normal', subject
=subject
, session
=session
,
3106 form_node
=form_node
)
3108 self
.subject_entry
.set_text('') # we sent ok, clear the subject
3109 self
.message_tv_buffer
.set_text('') # we sent ok, clear the textview
3111 def on_send_button_clicked(self
, widget
):
3112 self
.send_single_message()
3114 def on_reply_button_clicked(self
, widget
):
3115 # we create a new blank window to send and we preset RE: and to jid
3116 self
.subject
= _('RE: %s') % self
.subject
3117 self
.message
= _('%s wrote:\n') % self
.from_whom
+ self
.message
3118 # add > at the begining of each line
3119 self
.message
= self
.message
.replace('\n', '\n> ') + '\n\n'
3120 self
.window
.destroy()
3121 SingleMessageWindow(self
.account
, to
=self
.from_whom
, action
='send',
3122 from_whom
=self
.from_whom
, subject
=self
.subject
, message
=self
.message
,
3123 session
=self
.session
)
3125 def on_send_and_close_button_clicked(self
, widget
):
3126 self
.send_single_message()
3128 self
.window
.destroy()
3130 def on_single_message_window_key_press_event(self
, widget
, event
):
3131 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
3133 self
.window
.destroy()
3135 class XMLConsoleWindow
:
3136 def __init__(self
, account
):
3137 self
.account
= account
3139 self
.xml
= gtkgui_helpers
.get_gtk_builder('xml_console_window.ui')
3140 self
.window
= self
.xml
.get_object('xml_console_window')
3141 self
.input_textview
= self
.xml
.get_object('input_textview')
3142 self
.stanzas_log_textview
= self
.xml
.get_object('stanzas_log_textview')
3143 self
.input_tv_buffer
= self
.input_textview
.get_buffer()
3144 buffer_
= self
.stanzas_log_textview
.get_buffer()
3145 end_iter
= buffer_
.get_end_iter()
3146 buffer_
.create_mark('end', end_iter
, False)
3148 self
.tagIn
= buffer_
.create_tag('incoming')
3149 color
= gajim
.config
.get('inmsgcolor')
3150 self
.tagIn
.set_property('foreground', color
)
3151 self
.tagInPresence
= buffer_
.create_tag('incoming_presence')
3152 self
.tagInPresence
.set_property('foreground', color
)
3153 self
.tagInMessage
= buffer_
.create_tag('incoming_message')
3154 self
.tagInMessage
.set_property('foreground', color
)
3155 self
.tagInIq
= buffer_
.create_tag('incoming_iq')
3156 self
.tagInIq
.set_property('foreground', color
)
3158 self
.tagOut
= buffer_
.create_tag('outgoing')
3159 color
= gajim
.config
.get('outmsgcolor')
3160 self
.tagOut
.set_property('foreground', color
)
3161 self
.tagOutPresence
= buffer_
.create_tag('outgoing_presence')
3162 self
.tagOutPresence
.set_property('foreground', color
)
3163 self
.tagOutMessage
= buffer_
.create_tag('outgoing_message')
3164 self
.tagOutMessage
.set_property('foreground', color
)
3165 self
.tagOutIq
= buffer_
.create_tag('outgoing_iq')
3166 self
.tagOutIq
.set_property('foreground', color
)
3167 buffer_
.create_tag('') # Default tag
3170 self
.xml
.get_object('enable_checkbutton').set_active(True)
3172 self
.input_textview
.modify_text(
3173 gtk
.STATE_NORMAL
, gtk
.gdk
.color_parse(color
))
3175 if len(gajim
.connections
) > 1:
3176 title
= _('XML Console for %s') % self
.account
3178 title
= _('XML Console')
3180 self
.window
.set_title(title
)
3181 self
.window
.show_all()
3182 gajim
.ged
.register_event_handler('stanza-received', ged
.GUI1
,
3183 self
._nec
_stanza
_received
)
3184 gajim
.ged
.register_event_handler('stanza-sent', ged
.GUI1
,
3185 self
._nec
_stanza
_sent
)
3187 self
.xml
.connect_signals(self
)
3189 def on_xml_console_window_destroy(self
, widget
):
3190 del gajim
.interface
.instances
[self
.account
]['xml_console']
3191 gajim
.ged
.remove_event_handler('stanza-received', ged
.GUI1
,
3192 self
._nec
_stanza
_received
)
3193 gajim
.ged
.remove_event_handler('stanza-sent', ged
.GUI1
,
3194 self
._nec
_stanza
_sent
)
3196 def on_clear_button_clicked(self
, widget
):
3197 buffer_
= self
.stanzas_log_textview
.get_buffer()
3198 buffer_
.set_text('')
3200 def on_enable_checkbutton_toggled(self
, widget
):
3201 self
.enabled
= widget
.get_active()
3203 def on_in_stanza_checkbutton_toggled(self
, widget
):
3204 active
= widget
.get_active()
3205 self
.tagIn
.set_property('invisible', active
)
3206 self
.tagInPresence
.set_property('invisible', active
)
3207 self
.tagInMessage
.set_property('invisible', active
)
3208 self
.tagInIq
.set_property('invisible', active
)
3210 def on_presence_stanza_checkbutton_toggled(self
, widget
):
3211 active
= widget
.get_active()
3212 self
.tagInPresence
.set_property('invisible', active
)
3213 self
.tagOutPresence
.set_property('invisible', active
)
3215 def on_out_stanza_checkbutton_toggled(self
, widget
):
3216 active
= widget
.get_active()
3217 self
.tagOut
.set_property('invisible', active
)
3218 self
.tagOutPresence
.set_property('invisible', active
)
3219 self
.tagOutMessage
.set_property('invisible', active
)
3220 self
.tagOutIq
.set_property('invisible', active
)
3222 def on_message_stanza_checkbutton_toggled(self
, widget
):
3223 active
= widget
.get_active()
3224 self
.tagInMessage
.set_property('invisible', active
)
3225 self
.tagOutMessage
.set_property('invisible', active
)
3227 def on_iq_stanza_checkbutton_toggled(self
, widget
):
3228 active
= widget
.get_active()
3229 self
.tagInIq
.set_property('invisible', active
)
3230 self
.tagOutIq
.set_property('invisible', active
)
3232 def scroll_to_end(self
, ):
3233 parent
= self
.stanzas_log_textview
.get_parent()
3234 buffer_
= self
.stanzas_log_textview
.get_buffer()
3235 end_mark
= buffer_
.get_mark('end')
3238 self
.stanzas_log_textview
.scroll_to_mark(end_mark
, 0, True, 0, 1)
3239 adjustment
= parent
.get_hadjustment()
3240 adjustment
.set_value(0)
3243 def print_stanza(self
, stanza
, kind
):
3244 # kind must be 'incoming' or 'outgoing'
3245 if not self
.enabled
:
3250 buffer = self
.stanzas_log_textview
.get_buffer()
3252 end_iter
= buffer.get_end_iter()
3253 end_rect
= self
.stanzas_log_textview
.get_iter_location(end_iter
)
3254 visible_rect
= self
.stanzas_log_textview
.get_visible_rect()
3255 if end_rect
.y
<= (visible_rect
.y
+ visible_rect
.height
):
3257 end_iter
= buffer.get_end_iter()
3260 if stanza
[1:9] == 'presence':
3262 elif stanza
[1:8] == 'message':
3264 elif stanza
[1:3] == 'iq':
3268 type_
= kind
+ '_' + type_
3270 type_
= kind
# 'incoming' or 'outgoing'
3272 if kind
== 'incoming':
3273 buffer.insert_with_tags_by_name(end_iter
, '<!-- In -->\n', type_
)
3274 elif kind
== 'outgoing':
3275 buffer.insert_with_tags_by_name(end_iter
, '<!-- Out -->\n', type_
)
3276 end_iter
= buffer.get_end_iter()
3277 buffer.insert_with_tags_by_name(end_iter
, stanza
.replace('><', '>\n<') \
3280 gobject
.idle_add(self
.scroll_to_end
)
3282 def _nec_stanza_received(self
, obj
):
3283 if obj
.conn
.name
!= self
.account
:
3285 self
.print_stanza(obj
.stanza_str
, 'incoming')
3287 def _nec_stanza_sent(self
, obj
):
3288 if obj
.conn
.name
!= self
.account
:
3290 self
.print_stanza(obj
.stanza_str
, 'outgoing')
3292 def on_send_button_clicked(self
, widget
):
3293 if gajim
.connections
[self
.account
].connected
<= 1:
3294 # if offline or connecting
3295 ErrorDialog(_('Connection not available'),
3296 _('Please make sure you are connected with "%s".') % \
3299 begin_iter
, end_iter
= self
.input_tv_buffer
.get_bounds()
3300 stanza
= self
.input_tv_buffer
.get_text(begin_iter
, end_iter
).decode(
3303 gajim
.connections
[self
.account
].send_stanza(stanza
)
3304 self
.input_tv_buffer
.set_text('') # we sent ok, clear the textview
3306 def on_presence_button_clicked(self
, widget
):
3307 self
.input_tv_buffer
.set_text(
3308 '<presence><show></show><status></status><priority></priority>'
3311 def on_iq_button_clicked(self
, widget
):
3312 self
.input_tv_buffer
.set_text(
3313 '<iq to="" type=""><query xmlns=""></query></iq>')
3315 def on_message_button_clicked(self
, widget
):
3316 self
.input_tv_buffer
.set_text(
3317 '<message to="" type=""><body></body></message>')
3319 def on_expander_activate(self
, widget
):
3320 if not widget
.get_expanded(): # it's the opposite!
3322 self
.input_textview
.grab_focus()
3324 #Action that can be done with an incoming list of contacts
3325 TRANSLATED_ACTION
= {'add': _('add'), 'modify': _('modify'),
3326 'remove': _('remove')}
3327 class RosterItemExchangeWindow
:
3329 Windows used when someone send you a exchange contact suggestion
3332 def __init__(self
, account
, action
, exchange_list
, jid_from
,
3334 self
.account
= account
3335 self
.action
= action
3336 self
.exchange_list
= exchange_list
3337 self
.message_body
= message_body
3338 self
.jid_from
= jid_from
3342 # Connect to gtk builder
3343 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3344 'roster_item_exchange_window.ui')
3345 self
.window
= self
.xml
.get_object('roster_item_exchange_window')
3348 for widget_to_add
in ['accept_button_label', 'type_label',
3349 'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
3350 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3353 # self.action can be 'add', 'modify' or 'remove'
3354 self
.type_label
.set_label(
3355 _('<b>%(jid)s</b> would like you to <b>%(action)s</b> some contacts '
3356 'in your roster.') % {'jid': jid_from
,
3357 'action': TRANSLATED_ACTION
[self
.action
]})
3359 buffer_
= self
.body_textview
.get_buffer()
3360 buffer_
.set_text(self
.message_body
)
3362 self
.body_scrolledwindow
.hide()
3364 model
= gtk
.ListStore(bool, str, str, str, str)
3365 self
.items_list_treeview
.set_model(model
)
3367 renderer1
= gtk
.CellRendererToggle()
3368 renderer1
.set_property('activatable', True)
3369 renderer1
.connect('toggled', self
.toggled_callback
)
3370 if self
.action
== 'add':
3372 elif self
.action
== 'modify':
3374 elif self
.action
== 'delete':
3376 self
.items_list_treeview
.insert_column_with_attributes(-1, title
,
3377 renderer1
, active
=0)
3378 renderer2
= gtk
.CellRendererText()
3379 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Jabber ID'),
3381 renderer3
= gtk
.CellRendererText()
3382 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Name'),
3384 renderer4
= gtk
.CellRendererText()
3385 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Groups'),
3389 model
= self
.items_list_treeview
.get_model()
3393 for jid
in self
.exchange_list
:
3396 contact
= gajim
.contacts
.get_contact_with_highest_priority(
3399 is_in_roster
= False
3400 name
= self
.exchange_list
[jid
][0]
3401 num_list
= len(self
.exchange_list
[jid
][1])
3403 for group
in self
.exchange_list
[jid
][1]:
3405 if contact
and not group
in contact
.groups
:
3406 is_in_roster
= False
3407 if current
== num_list
:
3408 groups
= groups
+ group
3410 groups
= groups
+ group
+ ', '
3411 if not is_in_roster
:
3413 iter_
= model
.append()
3414 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3416 # Change label for accept_button to action name instead of 'OK'.
3417 self
.accept_button_label
.set_label(_('Add'))
3418 elif action
== 'modify':
3419 for jid
in self
.exchange_list
:
3423 contact
= gajim
.contacts
.get_contact_with_highest_priority(
3425 name
= self
.exchange_list
[jid
][0]
3427 is_in_roster
= False
3430 if name
!= contact
.name
:
3432 num_list
= len(self
.exchange_list
[jid
][1])
3434 for group
in self
.exchange_list
[jid
][1]:
3436 if contact
and not group
in contact
.groups
:
3438 if current
== num_list
:
3439 groups
= groups
+ group
3441 groups
= groups
+ group
+ ', '
3442 if not is_right
and is_in_roster
:
3444 iter_
= model
.append()
3445 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3447 # Change label for accept_button to action name instead of 'OK'.
3448 self
.accept_button_label
.set_label(_('Modify'))
3449 elif action
== 'delete':
3450 for jid
in self
.exchange_list
:
3453 contact
= gajim
.contacts
.get_contact_with_highest_priority(
3455 name
= self
.exchange_list
[jid
][0]
3457 is_in_roster
= False
3458 num_list
= len(self
.exchange_list
[jid
][1])
3460 for group
in self
.exchange_list
[jid
][1]:
3462 if current
== num_list
:
3463 groups
= groups
+ group
3465 groups
= groups
+ group
+ ', '
3468 iter_
= model
.append()
3469 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3471 # Change label for accept_button to action name instead of 'OK'.
3472 self
.accept_button_label
.set_label(_('Delete'))
3475 self
.window
.show_all()
3476 self
.xml
.connect_signals(self
)
3478 def toggled_callback(self
, cell
, path
):
3479 model
= self
.items_list_treeview
.get_model()
3480 iter_
= model
.get_iter(path
)
3481 model
[iter_
][0] = not cell
.get_active()
3483 def on_accept_button_clicked(self
, widget
):
3484 model
= self
.items_list_treeview
.get_model()
3485 iter_
= model
.get_iter_root()
3486 if self
.action
== 'add':
3492 #remote_jid = model[iter_][1].decode('utf-8')
3493 message
= _('%s suggested me to add you in my roster.'
3495 # keep same groups and same nickname
3496 groups
= model
[iter_
][3].split(', ')
3499 jid
= model
[iter_
][1].decode('utf-8')
3500 if gajim
.jid_is_transport(self
.jid_from
):
3501 gajim
.connections
[self
.account
].automatically_added
.append(
3503 gajim
.interface
.roster
.req_sub(self
, jid
, message
,
3504 self
.account
, groups
=groups
, nickname
=model
[iter_
][2],
3506 iter_
= model
.iter_next(iter_
)
3507 InformationDialog(_('Added %s contacts') % str(a
))
3508 elif self
.action
== 'modify':
3514 jid
= model
[iter_
][1].decode('utf-8')
3515 # keep same groups and same nickname
3516 groups
= model
[iter_
][3].split(', ')
3519 for u
in gajim
.contacts
.get_contact(self
.account
, jid
):
3520 u
.name
= model
[iter_
][2]
3521 gajim
.connections
[self
.account
].update_contact(jid
,
3522 model
[iter_
][2], groups
)
3523 self
.draw_contact(jid
, self
.account
)
3524 # Update opened chat
3525 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(jid
, self
.account
)
3528 win
= gajim
.interface
.msg_win_mgr
.get_window(jid
,
3530 win
.redraw_tab(ctrl
)
3532 iter_
= model
.iter_next(iter_
)
3533 elif self
.action
== 'delete':
3539 jid
= model
[iter_
][1].decode('utf-8')
3540 gajim
.connections
[self
.account
].unsubscribe(jid
)
3541 gajim
.interface
.roster
.remove_contact(jid
, self
.account
)
3542 gajim
.contacts
.remove_jid(self
.account
, jid
)
3543 iter_
= model
.iter_next(iter_
)
3544 InformationDialog(_('Removed %s contacts') % str(a
))
3545 self
.window
.destroy()
3547 def on_cancel_button_clicked(self
, widget
):
3548 self
.window
.destroy()
3551 class ItemArchivingPreferencesWindow
:
3552 otr_name
= ('approve', 'concede', 'forbid', 'oppose', 'prefer', 'require')
3553 otr_index
= dict([(j
, i
) for i
, j
in enumerate(otr_name
)])
3554 save_name
= ('body', 'false', 'message', 'stream')
3555 save_index
= dict([(j
, i
) for i
, j
in enumerate(save_name
)])
3557 def __init__(self
, account
, item
):
3558 self
.account
= account
3560 if self
.item
and self
.item
!= 'Default':
3561 self
.item_config
= gajim
.connections
[self
.account
].items
[self
.item
]
3563 self
.item_config
= gajim
.connections
[self
.account
].default
3566 # Connect to gtk builder
3567 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3568 'item_archiving_preferences_window.ui')
3569 self
.window
= self
.xml
.get_object('item_archiving_preferences_window')
3572 for widget_to_add
in ('jid_entry', 'expire_entry', 'otr_combobox',
3573 'save_combobox', 'cancel_button', 'ok_button', 'progressbar'):
3574 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3577 self
.jid_entry
.set_text(self
.item
)
3578 expire_value
= self
.item_config
['expire'] or ''
3579 self
.otr_combobox
.set_active(self
.otr_index
[self
.item_config
['otr']])
3580 self
.save_combobox
.set_active(
3581 self
.save_index
[self
.item_config
['save']])
3582 self
.expire_entry
.set_text(expire_value
)
3584 self
.window
.set_title(_('Archiving Preferences for %s') % self
.account
)
3586 self
.window
.show_all()
3587 self
.progressbar
.hide()
3588 self
.xml
.connect_signals(self
)
3590 def update_progressbar(self
):
3592 self
.progressbar
.pulse()
3596 def on_otr_combobox_changed(self
, widget
):
3597 otr
= self
.otr_name
[self
.otr_combobox
.get_active()]
3598 if otr
== 'require':
3599 self
.save_combobox
.set_active(self
.save_index
['false'])
3601 def on_ok_button_clicked(self
, widget
):
3602 # Return directly if operation in progress
3606 item
= self
.jid_entry
.get_text()
3607 otr
= self
.otr_name
[self
.otr_combobox
.get_active()]
3608 save
= self
.save_name
[self
.save_combobox
.get_active()]
3609 expire
= self
.expire_entry
.get_text()
3611 if self
.item
!= 'Default':
3613 item
= helpers
.parse_jid(item
)
3614 except helpers
.InvalidFormat
, s
:
3615 pritext
= _('Invalid User ID')
3616 ErrorDialog(pritext
, str(s
))
3621 if int(expire
) < 0 or str(int(expire
)) != expire
:
3624 pritext
= _('Invalid expire value')
3625 sectext
= _('Expire must be a valid positive integer.')
3626 ErrorDialog(pritext
, sectext
)
3629 if not (item
== self
.item
and expire
== self
.item_config
['expire'] and
3630 otr
== self
.item_config
['otr'] and save
== self
.item_config
['save']):
3631 if not self
.item
or self
.item
== item
:
3632 if self
.item
== 'Default':
3633 self
.waiting
= 'default'
3634 gajim
.connections
[self
.account
].set_default(
3637 self
.waiting
= 'item'
3638 gajim
.connections
[self
.account
].append_or_update_item(
3639 item
, otr
, save
, expire
)
3641 self
.waiting
= 'item'
3642 gajim
.connections
[self
.account
].append_or_update_item(
3643 item
, otr
, save
, expire
)
3644 gajim
.connections
[self
.account
].remove_item(self
.item
)
3645 self
.launch_progressbar()
3646 #self.window.destroy()
3648 def on_cancel_button_clicked(self
, widget
):
3649 self
.window
.destroy()
3651 def on_item_archiving_preferences_window_destroy(self
, widget
):
3653 key_name
= 'edit_item_archiving_preferences_%s' % self
.item
3655 key_name
= 'new_item_archiving_preferences'
3656 if key_name
in gajim
.interface
.instances
[self
.account
]:
3657 del gajim
.interface
.instances
[self
.account
][key_name
]
3659 def launch_progressbar(self
):
3660 self
.progressbar
.show()
3661 self
.update_progressbar_timeout_id
= gobject
.timeout_add(
3662 100, self
.update_progressbar
)
3664 def response_arrived(self
, data
):
3666 self
.window
.destroy()
3668 def error_arrived(self
, error
):
3671 self
.progressbar
.hide()
3672 pritext
= _('There is an error with the form')
3674 ErrorDialog(pritext
, sectext
)
3677 class ArchivingPreferencesWindow
:
3678 auto_name
= ('false', 'true')
3679 auto_index
= dict([(j
, i
) for i
, j
in enumerate(auto_name
)])
3680 method_foo_name
= ('prefer', 'concede', 'forbid')
3681 method_foo_index
= dict([(j
, i
) for i
, j
in enumerate(method_foo_name
)])
3683 def __init__(self
, account
):
3684 self
.account
= account
3688 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3689 'archiving_preferences_window.ui')
3690 self
.window
= self
.xml
.get_object('archiving_preferences_window')
3693 for widget_to_add
in ('auto_combobox', 'method_auto_combobox',
3694 'method_local_combobox', 'method_manual_combobox', 'close_button',
3695 'item_treeview', 'item_notebook', 'otr_combobox', 'save_combobox',
3696 'expire_entry', 'remove_button', 'edit_button'):
3697 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3699 self
.auto_combobox
.set_active(
3700 self
.auto_index
[gajim
.connections
[self
.account
].auto
])
3701 self
.method_auto_combobox
.set_active(
3702 self
.method_foo_index
[gajim
.connections
[self
.account
].method_auto
])
3703 self
.method_local_combobox
.set_active(
3704 self
.method_foo_index
[gajim
.connections
[self
.account
].method_local
])
3705 self
.method_manual_combobox
.set_active(
3706 self
.method_foo_index
[gajim
.connections
[self
.account
].\
3709 model
= gtk
.ListStore(str, str, str, str)
3710 self
.item_treeview
.set_model(model
)
3711 col
= gtk
.TreeViewColumn('jid')
3712 self
.item_treeview
.append_column(col
)
3713 renderer
= gtk
.CellRendererText()
3714 col
.pack_start(renderer
, True)
3715 col
.set_attributes(renderer
, text
=0)
3717 col
= gtk
.TreeViewColumn('expire')
3718 col
.pack_start(renderer
, True)
3719 col
.set_attributes(renderer
, text
=1)
3720 self
.item_treeview
.append_column(col
)
3722 col
= gtk
.TreeViewColumn('otr')
3723 col
.pack_start(renderer
, True)
3724 col
.set_attributes(renderer
, text
=2)
3725 self
.item_treeview
.append_column(col
)
3727 col
= gtk
.TreeViewColumn('save')
3728 col
.pack_start(renderer
, True)
3729 col
.set_attributes(renderer
, text
=3)
3730 self
.item_treeview
.append_column(col
)
3734 self
.current_item
= None
3736 def sort_items(model
, iter1
, iter2
):
3737 item1
= model
.get_value(iter1
, 0)
3738 item2
= model
.get_value(iter2
, 0)
3739 if item1
== 'Default':
3741 if item2
== 'Default':
3744 if '@' not in item2
:
3752 # item1 == item2 ? WTF?
3755 model
.set_sort_column_id(0, gtk
.SORT_ASCENDING
)
3756 model
.set_sort_func(0, sort_items
)
3758 self
.remove_button
.set_sensitive(False)
3759 self
.edit_button
.set_sensitive(False)
3761 self
.window
.set_title(_('Archiving Preferences for %s') % self
.account
)
3763 gajim
.ged
.register_event_handler(
3764 'archiving-preferences-changed-received', ged
.GUI1
,
3765 self
._nec
_archiving
_changed
_received
)
3766 gajim
.ged
.register_event_handler('archiving-error-received', ged
.GUI1
,
3767 self
._nec
_archiving
_error
)
3769 self
.window
.show_all()
3771 self
.xml
.connect_signals(self
)
3773 def on_add_item_button_clicked(self
, widget
):
3774 key_name
= 'new_item_archiving_preferences'
3775 if key_name
in gajim
.interface
.instances
[self
.account
]:
3776 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
3778 gajim
.interface
.instances
[self
.account
][key_name
] = \
3779 ItemArchivingPreferencesWindow(self
.account
, '')
3781 def on_remove_item_button_clicked(self
, widget
):
3782 if not self
.current_item
:
3785 self
.waiting
.append('itemremove')
3786 sel
= self
.item_treeview
.get_selection()
3787 (model
, iter_
) = sel
.get_selected()
3788 gajim
.connections
[self
.account
].remove_item(model
[iter_
][0])
3790 self
.remove_button
.set_sensitive(False)
3791 self
.edit_button
.set_sensitive(False)
3793 def on_edit_item_button_clicked(self
, widget
):
3794 if not self
.current_item
:
3797 key_name
= 'edit_item_archiving_preferences_%s' % self
.current_item
3798 if key_name
in gajim
.interface
.instances
[self
.account
]:
3799 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
3801 gajim
.interface
.instances
[self
.account
][key_name
] = \
3802 ItemArchivingPreferencesWindow(self
.account
, self
.current_item
)
3804 def on_item_treeview_cursor_changed(self
, widget
):
3805 sel
= self
.item_treeview
.get_selection()
3806 (model
, iter_
) = sel
.get_selected()
3809 item
= model
[iter_
][0]
3810 if self
.current_item
and self
.current_item
== item
:
3813 self
.current_item
= item
3814 if self
.current_item
== 'Default':
3815 self
.remove_button
.set_sensitive(False)
3816 self
.edit_button
.set_sensitive(True)
3817 elif self
.current_item
:
3818 self
.remove_button
.set_sensitive(True)
3819 self
.edit_button
.set_sensitive(True)
3821 self
.remove_button
.set_sensitive(False)
3822 self
.edit_button
.set_sensitive(False)
3824 def on_auto_combobox_changed(self
, widget
):
3825 save
= self
.auto_name
[widget
.get_active()]
3826 gajim
.connections
[self
.account
].set_auto(save
)
3828 def on_method_foo_combobox_changed(self
, widget
):
3829 # We retrieve method type from widget name
3830 # ('foo' in 'method_foo_combobox')
3831 method_type
= widget
.name
.split('_')[1]
3832 use
= self
.method_foo_name
[widget
.get_active()]
3833 self
.waiting
.append('method_%s' % method_type
)
3834 gajim
.connections
[self
.account
].set_method(method_type
, use
)
3836 def get_child_window(self
):
3837 edit_key_name
= 'edit_item_archiving_preferences_%s' % self
.current_item
3838 new_key_name
= 'new_item_archiving_preferences'
3840 if edit_key_name
in gajim
.interface
.instances
[self
.account
]:
3841 return gajim
.interface
.instances
[self
.account
][edit_key_name
]
3843 if new_key_name
in gajim
.interface
.instances
[self
.account
]:
3844 return gajim
.interface
.instances
[self
.account
][new_key_name
]
3846 def _nec_archiving_changed_received(self
, obj
):
3847 if obj
.conn
.name
!= self
.account
:
3849 for key
in ('auto', 'method_auto', 'method_local', 'method_manual'):
3850 if key
in obj
.conf
and key
in self
.waiting
:
3851 self
.waiting
.remove(key
)
3852 if 'default' in obj
.conf
:
3853 key_name
= 'edit_item_archiving_preferences_%s' % \
3855 if key_name
in gajim
.interface
.instances
[self
.account
]:
3856 gajim
.interface
.instances
[self
.account
][key_name
].\
3857 response_arrived(obj
.conf
['default'])
3858 self
.fill_items(True)
3859 for jid
, pref
in obj
.new_items
.items():
3860 child
= self
.get_child_window()
3862 is_new
= not child
.item
3863 child
.response_arrived(pref
)
3865 model
= self
.item_treeview
.get_model()
3866 model
.append((jid
, pref
['expire'], pref
['otr'],
3869 self
.fill_items(True)
3870 if 'itemremove' in self
.waiting
and obj
.removed_items
:
3871 self
.waiting
.remove('itemremove')
3872 self
.fill_items(True)
3874 def fill_items(self
, clear
=False):
3875 model
= self
.item_treeview
.get_model()
3878 default_config
= gajim
.connections
[self
.account
].default
3879 expire_value
= default_config
['expire'] or ''
3880 model
.append(('Default', expire_value
,
3881 default_config
['otr'], default_config
['save']))
3882 for item
, item_config
in \
3883 gajim
.connections
[self
.account
].items
.items():
3884 expire_value
= item_config
['expire'] or ''
3885 model
.append((item
, expire_value
, item_config
['otr'],
3886 item_config
['save']))
3888 def _nec_archiving_error(self
, obj
):
3889 if obj
.conn
.name
!= self
.account
:
3892 pritext
= _('There is an error')
3893 sectext
= obj
.error_msg
3894 ErrorDialog(pritext
, sectext
)
3897 child
= self
.get_child_window()
3899 child
.error_arrived(obj
.error_msg
)
3901 def on_close_button_clicked(self
, widget
):
3902 self
.window
.destroy()
3904 def on_archiving_preferences_window_destroy(self
, widget
):
3905 gajim
.ged
.remove_event_handler(
3906 'archiving-preferences-changed-received', ged
.GUI1
,
3907 self
._nec
_archiving
_changed
_received
)
3908 gajim
.ged
.remove_event_handler('archiving-error-received', ged
.GUI1
,
3909 self
._nec
_archiving
_error
)
3910 if 'archiving_preferences' in gajim
.interface
.instances
[self
.account
]:
3911 del gajim
.interface
.instances
[self
.account
]['archiving_preferences']
3914 class PrivacyListWindow
:
3916 Window that is used for creating NEW or EDITING already there privacy lists
3919 def __init__(self
, account
, privacy_list_name
, action
):
3920 '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
3921 or edit an already existing one'''
3922 self
.account
= account
3923 self
.privacy_list_name
= privacy_list_name
3925 # Dicts and Default Values
3926 self
.active_rule
= ''
3927 self
.global_rules
= {}
3928 self
.list_of_groups
= {}
3932 # Default Edit Values
3933 self
.edit_rule_type
= 'jid'
3934 self
.allow_deny
= 'allow'
3936 # Connect to gtk builder
3937 self
.xml
= gtkgui_helpers
.get_gtk_builder('privacy_list_window.ui')
3938 self
.window
= self
.xml
.get_object('privacy_list_edit_window')
3942 for widget_to_add
in ('title_hbox', 'privacy_lists_title_label',
3943 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
3944 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
3945 'list_of_rules_combobox', 'delete_open_buttons_hbox',
3946 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
3947 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
3948 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
3949 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
3950 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
3951 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
3952 'edit_view_status_checkbutton', 'edit_all_checkbutton',
3953 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
3954 'privacy_list_refresh_button', 'privacy_list_close_button',
3955 'edit_send_status_checkbutton', 'add_edit_vbox',
3956 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
3957 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3959 self
.privacy_lists_title_label
.set_label(
3960 _('Privacy List <b><i>%s</i></b>') % \
3961 gobject
.markup_escape_text(self
.privacy_list_name
))
3963 if len(gajim
.connections
) > 1:
3964 title
= _('Privacy List for %s') % self
.account
3966 title
= _('Privacy List')
3968 self
.delete_rule_button
.set_sensitive(False)
3969 self
.open_rule_button
.set_sensitive(False)
3970 self
.privacy_list_active_checkbutton
.set_sensitive(False)
3971 self
.privacy_list_default_checkbutton
.set_sensitive(False)
3972 self
.list_of_rules_combobox
.set_sensitive(False)
3974 # set jabber id completion
3975 jids_list_store
= gtk
.ListStore(gobject
.TYPE_STRING
)
3976 for jid
in gajim
.contacts
.get_jid_list(self
.account
):
3977 jids_list_store
.append([jid
])
3978 jid_entry_completion
= gtk
.EntryCompletion()
3979 jid_entry_completion
.set_text_column(0)
3980 jid_entry_completion
.set_model(jids_list_store
)
3981 jid_entry_completion
.set_popup_completion(True)
3982 self
.edit_type_jabberid_entry
.set_completion(jid_entry_completion
)
3983 if action
== 'EDIT':
3984 self
.refresh_rules()
3987 for group
in gajim
.groups
[self
.account
]:
3988 self
.list_of_groups
[group
] = count
3990 self
.edit_type_group_combobox
.append_text(group
)
3991 self
.edit_type_group_combobox
.set_active(0)
3993 self
.window
.set_title(title
)
3995 gajim
.ged
.register_event_handler('privacy-list-received', ged
.GUI1
,
3996 self
._nec
_privacy
_list
_received
)
3997 gajim
.ged
.register_event_handler('privacy-list-active-default',
3998 ged
.GUI1
, self
._nec
_privacy
_list
_active
_default
)
4000 self
.window
.show_all()
4001 self
.add_edit_vbox
.hide()
4003 self
.xml
.connect_signals(self
)
4005 def on_privacy_list_edit_window_destroy(self
, widget
):
4006 key_name
= 'privacy_list_%s' % self
.privacy_list_name
4007 if key_name
in gajim
.interface
.instances
[self
.account
]:
4008 del gajim
.interface
.instances
[self
.account
][key_name
]
4009 gajim
.ged
.remove_event_handler('privacy-list-received', ged
.GUI1
,
4010 self
._nec
_privacy
_list
_received
)
4011 gajim
.ged
.remove_event_handler('privacy-list-active-default',
4012 ged
.GUI1
, self
._nec
_privacy
_list
_active
_default
)
4014 def _nec_privacy_list_active_default(self
, obj
):
4015 if obj
.conn
.name
!= self
.account
:
4017 if obj
.active_list
== self
.privacy_list_name
:
4018 self
.privacy_list_active_checkbutton
.set_active(True)
4020 self
.privacy_list_active_checkbutton
.set_active(False)
4021 if obj
.default_list
== self
.privacy_list_name
:
4022 self
.privacy_list_default_checkbutton
.set_active(True)
4024 self
.privacy_list_default_checkbutton
.set_active(False)
4026 def privacy_list_received(self
, rules
):
4027 self
.list_of_rules_combobox
.get_model().clear()
4028 self
.global_rules
= {}
4031 text_item
= _('Order: %(order)s, action: %(action)s, type: %(type)s'
4032 ', value: %(value)s') % {'order': rule
['order'],
4033 'action': rule
['action'], 'type': rule
['type'],
4034 'value': rule
['value']}
4036 text_item
= _('Order: %(order)s, action: %(action)s') % \
4037 {'order': rule
['order'], 'action': rule
['action']}
4038 if int(rule
['order']) > self
.max_order
:
4039 self
.max_order
= int(rule
['order'])
4040 self
.global_rules
[text_item
] = rule
4041 self
.list_of_rules_combobox
.append_text(text_item
)
4043 self
.title_hbox
.set_sensitive(False)
4044 self
.list_of_rules_combobox
.set_sensitive(False)
4045 self
.delete_rule_button
.set_sensitive(False)
4046 self
.open_rule_button
.set_sensitive(False)
4047 self
.privacy_list_active_checkbutton
.set_sensitive(False)
4048 self
.privacy_list_default_checkbutton
.set_sensitive(False)
4050 self
.list_of_rules_combobox
.set_active(0)
4051 self
.title_hbox
.set_sensitive(True)
4052 self
.list_of_rules_combobox
.set_sensitive(True)
4053 self
.delete_rule_button
.set_sensitive(True)
4054 self
.open_rule_button
.set_sensitive(True)
4055 self
.privacy_list_active_checkbutton
.set_sensitive(True)
4056 self
.privacy_list_default_checkbutton
.set_sensitive(True)
4058 gajim
.connections
[self
.account
].get_active_default_lists()
4060 def _nec_privacy_list_received(self
, obj
):
4061 if obj
.conn
.name
!= self
.account
:
4063 if obj
.list_name
!= self
.privacy_list_name
:
4065 self
.privacy_list_received(obj
.rules
)
4067 def refresh_rules(self
):
4068 gajim
.connections
[self
.account
].get_privacy_list(self
.privacy_list_name
)
4070 def on_delete_rule_button_clicked(self
, widget
):
4072 for rule
in self
.global_rules
:
4073 if rule
!= self
.list_of_rules_combobox
.get_active_text():
4074 tags
.append(self
.global_rules
[rule
])
4075 gajim
.connections
[self
.account
].set_privacy_list(
4076 self
.privacy_list_name
, tags
)
4077 self
.privacy_list_received(tags
)
4078 self
.add_edit_vbox
.hide()
4079 if not tags
: # we removed latest rule
4080 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4081 win
= gajim
.interface
.instances
[self
.account
]['privacy_lists']
4082 win
.remove_privacy_list_from_combobox(self
.privacy_list_name
)
4085 def on_open_rule_button_clicked(self
, widget
):
4086 self
.add_edit_rule_label
.set_label(
4087 _('<b>Edit a rule</b>'))
4088 active_num
= self
.list_of_rules_combobox
.get_active()
4089 if active_num
== -1:
4090 self
.active_rule
= ''
4092 self
.active_rule
= \
4093 self
.list_of_rules_combobox
.get_active_text().decode('utf-8')
4094 if self
.active_rule
!= '':
4095 rule_info
= self
.global_rules
[self
.active_rule
]
4096 self
.edit_order_spinbutton
.set_value(int(rule_info
['order']))
4097 if 'type' in rule_info
:
4098 if rule_info
['type'] == 'jid':
4099 self
.edit_type_jabberid_radiobutton
.set_active(True)
4100 self
.edit_type_jabberid_entry
.set_text(rule_info
['value'])
4101 elif rule_info
['type'] == 'group':
4102 self
.edit_type_group_radiobutton
.set_active(True)
4103 if rule_info
['value'] in self
.list_of_groups
:
4104 self
.edit_type_group_combobox
.set_active(
4105 self
.list_of_groups
[rule_info
['value']])
4107 self
.edit_type_group_combobox
.set_active(0)
4108 elif rule_info
['type'] == 'subscription':
4109 self
.edit_type_subscription_radiobutton
.set_active(True)
4110 sub_value
= rule_info
['value']
4111 if sub_value
== 'none':
4112 self
.edit_type_subscription_combobox
.set_active(0)
4113 elif sub_value
== 'both':
4114 self
.edit_type_subscription_combobox
.set_active(1)
4115 elif sub_value
== 'from':
4116 self
.edit_type_subscription_combobox
.set_active(2)
4117 elif sub_value
== 'to':
4118 self
.edit_type_subscription_combobox
.set_active(3)
4120 self
.edit_type_select_all_radiobutton
.set_active(True)
4122 self
.edit_type_select_all_radiobutton
.set_active(True)
4123 self
.edit_send_messages_checkbutton
.set_active(False)
4124 self
.edit_queries_send_checkbutton
.set_active(False)
4125 self
.edit_view_status_checkbutton
.set_active(False)
4126 self
.edit_send_status_checkbutton
.set_active(False)
4127 self
.edit_all_checkbutton
.set_active(False)
4128 if not rule_info
['child']:
4129 self
.edit_all_checkbutton
.set_active(True)
4131 if 'presence-out' in rule_info
['child']:
4132 self
.edit_send_status_checkbutton
.set_active(True)
4133 if 'presence-in' in rule_info
['child']:
4134 self
.edit_view_status_checkbutton
.set_active(True)
4135 if 'iq' in rule_info
['child']:
4136 self
.edit_queries_send_checkbutton
.set_active(True)
4137 if 'message' in rule_info
['child']:
4138 self
.edit_send_messages_checkbutton
.set_active(True)
4140 if rule_info
['action'] == 'allow':
4141 self
.edit_allow_radiobutton
.set_active(True)
4143 self
.edit_deny_radiobutton
.set_active(True)
4144 self
.add_edit_vbox
.show()
4146 def on_edit_all_checkbutton_toggled(self
, widget
):
4147 if widget
.get_active():
4148 self
.edit_send_messages_checkbutton
.set_active(True)
4149 self
.edit_queries_send_checkbutton
.set_active(True)
4150 self
.edit_view_status_checkbutton
.set_active(True)
4151 self
.edit_send_status_checkbutton
.set_active(True)
4152 self
.edit_send_messages_checkbutton
.set_sensitive(False)
4153 self
.edit_queries_send_checkbutton
.set_sensitive(False)
4154 self
.edit_view_status_checkbutton
.set_sensitive(False)
4155 self
.edit_send_status_checkbutton
.set_sensitive(False)
4157 self
.edit_send_messages_checkbutton
.set_active(False)
4158 self
.edit_queries_send_checkbutton
.set_active(False)
4159 self
.edit_view_status_checkbutton
.set_active(False)
4160 self
.edit_send_status_checkbutton
.set_active(False)
4161 self
.edit_send_messages_checkbutton
.set_sensitive(True)
4162 self
.edit_queries_send_checkbutton
.set_sensitive(True)
4163 self
.edit_view_status_checkbutton
.set_sensitive(True)
4164 self
.edit_send_status_checkbutton
.set_sensitive(True)
4166 def on_privacy_list_active_checkbutton_toggled(self
, widget
):
4167 if widget
.get_active():
4168 gajim
.connections
[self
.account
].set_active_list(
4169 self
.privacy_list_name
)
4171 gajim
.connections
[self
.account
].set_active_list(None)
4173 def on_privacy_list_default_checkbutton_toggled(self
, widget
):
4174 if widget
.get_active():
4175 gajim
.connections
[self
.account
].set_default_list(
4176 self
.privacy_list_name
)
4178 gajim
.connections
[self
.account
].set_default_list(None)
4180 def on_new_rule_button_clicked(self
, widget
):
4182 self
.add_edit_vbox
.show()
4184 def reset_fields(self
):
4185 self
.edit_type_jabberid_entry
.set_text('')
4186 self
.edit_allow_radiobutton
.set_active(True)
4187 self
.edit_type_jabberid_radiobutton
.set_active(True)
4188 self
.active_rule
= ''
4189 self
.edit_send_messages_checkbutton
.set_active(False)
4190 self
.edit_queries_send_checkbutton
.set_active(False)
4191 self
.edit_view_status_checkbutton
.set_active(False)
4192 self
.edit_send_status_checkbutton
.set_active(False)
4193 self
.edit_all_checkbutton
.set_active(False)
4194 self
.edit_order_spinbutton
.set_value(self
.max_order
+ 1)
4195 self
.edit_type_group_combobox
.set_active(0)
4196 self
.edit_type_subscription_combobox
.set_active(0)
4197 self
.add_edit_rule_label
.set_label(
4198 _('<b>Add a rule</b>'))
4200 def get_current_tags(self
):
4201 if self
.edit_type_jabberid_radiobutton
.get_active():
4203 edit_value
= self
.edit_type_jabberid_entry
.get_text()
4204 elif self
.edit_type_group_radiobutton
.get_active():
4206 edit_value
= self
.edit_type_group_combobox
.get_active_text()
4207 elif self
.edit_type_subscription_radiobutton
.get_active():
4208 edit_type
= 'subscription'
4209 subs
= ['none', 'both', 'from', 'to']
4210 edit_value
= subs
[self
.edit_type_subscription_combobox
.get_active()]
4211 elif self
.edit_type_select_all_radiobutton
.get_active():
4214 edit_order
= str(self
.edit_order_spinbutton
.get_value_as_int())
4215 if self
.edit_allow_radiobutton
.get_active():
4220 if not self
.edit_all_checkbutton
.get_active():
4221 if self
.edit_send_messages_checkbutton
.get_active():
4222 child
.append('message')
4223 if self
.edit_queries_send_checkbutton
.get_active():
4225 if self
.edit_send_status_checkbutton
.get_active():
4226 child
.append('presence-out')
4227 if self
.edit_view_status_checkbutton
.get_active():
4228 child
.append('presence-in')
4230 return {'order': edit_order
, 'action': edit_deny
,
4231 'type': edit_type
, 'value': edit_value
, 'child': child
}
4232 return {'order': edit_order
, 'action': edit_deny
, 'child': child
}
4234 def on_save_rule_button_clicked(self
, widget
):
4236 current_tags
= self
.get_current_tags()
4237 if int(current_tags
['order']) > self
.max_order
:
4238 self
.max_order
= int(current_tags
['order'])
4239 if self
.active_rule
== '':
4240 tags
.append(current_tags
)
4242 for rule
in self
.global_rules
:
4243 if rule
!= self
.active_rule
:
4244 tags
.append(self
.global_rules
[rule
])
4246 tags
.append(current_tags
)
4248 gajim
.connections
[self
.account
].set_privacy_list(
4249 self
.privacy_list_name
, tags
)
4250 self
.refresh_rules()
4251 self
.add_edit_vbox
.hide()
4252 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4253 win
= gajim
.interface
.instances
[self
.account
]['privacy_lists']
4254 win
.add_privacy_list_to_combobox(self
.privacy_list_name
)
4257 def on_list_of_rules_combobox_changed(self
, widget
):
4258 self
.add_edit_vbox
.hide()
4260 def on_edit_type_radiobutton_changed(self
, widget
, radiobutton
):
4261 active_bool
= widget
.get_active()
4263 self
.edit_rule_type
= radiobutton
4265 def on_edit_allow_radiobutton_changed(self
, widget
, radiobutton
):
4266 active_bool
= widget
.get_active()
4268 self
.allow_deny
= radiobutton
4270 def on_close_button_clicked(self
, widget
):
4271 self
.window
.destroy()
4273 class PrivacyListsWindow
:
4275 Window that is the main window for Privacy Lists; we can list there the
4276 privacy lists and ask to create a new one or edit an already there one
4278 def __init__(self
, account
):
4279 self
.account
= account
4280 self
.privacy_lists_save
= []
4282 self
.xml
= gtkgui_helpers
.get_gtk_builder('privacy_lists_window.ui')
4284 self
.window
= self
.xml
.get_object('privacy_lists_first_window')
4285 for widget_to_add
in ('list_of_privacy_lists_combobox',
4286 'delete_privacy_list_button', 'open_privacy_list_button',
4287 'new_privacy_list_button', 'new_privacy_list_entry',
4288 'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
4289 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
4291 self
.draw_privacy_lists_in_combobox([])
4292 self
.privacy_lists_refresh()
4296 if len(gajim
.connections
) > 1:
4297 title
= _('Privacy Lists for %s') % self
.account
4299 title
= _('Privacy Lists')
4301 self
.window
.set_title(title
)
4303 gajim
.ged
.register_event_handler('privacy-lists-received', ged
.GUI1
,
4304 self
._nec
_privacy
_lists
_received
)
4305 gajim
.ged
.register_event_handler('privacy-lists-removed', ged
.GUI1
,
4306 self
._nec
_privacy
_lists
_removed
)
4308 self
.window
.show_all()
4310 self
.xml
.connect_signals(self
)
4312 def on_privacy_lists_first_window_destroy(self
, widget
):
4313 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4314 del gajim
.interface
.instances
[self
.account
]['privacy_lists']
4315 gajim
.ged
.remove_event_handler('privacy-lists-received', ged
.GUI1
,
4316 self
._nec
_privacy
_lists
_received
)
4317 gajim
.ged
.remove_event_handler('privacy-lists-removed', ged
.GUI1
,
4318 self
._nec
_privacy
_lists
_removed
)
4320 def remove_privacy_list_from_combobox(self
, privacy_list
):
4321 if privacy_list
not in self
.privacy_lists_save
:
4323 privacy_list_index
= self
.privacy_lists_save
.index(privacy_list
)
4324 self
.list_of_privacy_lists_combobox
.remove_text(privacy_list_index
)
4325 self
.privacy_lists_save
.remove(privacy_list
)
4327 def add_privacy_list_to_combobox(self
, privacy_list
):
4328 if privacy_list
in self
.privacy_lists_save
:
4330 self
.list_of_privacy_lists_combobox
.append_text(privacy_list
)
4331 self
.privacy_lists_save
.append(privacy_list
)
4333 def draw_privacy_lists_in_combobox(self
, privacy_lists
):
4334 self
.list_of_privacy_lists_combobox
.set_active(-1)
4335 self
.list_of_privacy_lists_combobox
.get_model().clear()
4336 self
.privacy_lists_save
= []
4337 for add_item
in privacy_lists
:
4338 self
.add_privacy_list_to_combobox(add_item
)
4341 def draw_widgets(self
):
4342 if len(self
.privacy_lists_save
) == 0:
4343 self
.list_of_privacy_lists_combobox
.set_sensitive(False)
4344 self
.open_privacy_list_button
.set_sensitive(False)
4345 self
.delete_privacy_list_button
.set_sensitive(False)
4347 self
.list_of_privacy_lists_combobox
.set_sensitive(True)
4348 self
.list_of_privacy_lists_combobox
.set_active(0)
4349 self
.open_privacy_list_button
.set_sensitive(True)
4350 self
.delete_privacy_list_button
.set_sensitive(True)
4352 def on_close_button_clicked(self
, widget
):
4353 self
.window
.destroy()
4355 def on_delete_privacy_list_button_clicked(self
, widget
):
4356 active_list
= self
.privacy_lists_save
[
4357 self
.list_of_privacy_lists_combobox
.get_active()]
4358 gajim
.connections
[self
.account
].del_privacy_list(active_list
)
4360 def privacy_list_removed(self
, active_list
):
4361 self
.privacy_lists_save
.remove(active_list
)
4362 self
.privacy_lists_received({'lists': self
.privacy_lists_save
})
4364 def _nec_privacy_lists_removed(self
, obj
):
4365 if obj
.conn
.name
!= self
.account
:
4367 self
.privacy_list_removed(obj
.lists_list
)
4369 def privacy_lists_received(self
, lists
):
4373 for privacy_list
in lists
['lists']:
4374 privacy_lists
.append(privacy_list
)
4375 self
.draw_privacy_lists_in_combobox(privacy_lists
)
4377 def _nec_privacy_lists_received(self
, obj
):
4378 if obj
.conn
.name
!= self
.account
:
4380 self
.privacy_lists_received(obj
.lists_list
)
4382 def privacy_lists_refresh(self
):
4383 gajim
.connections
[self
.account
].get_privacy_lists()
4385 def on_new_privacy_list_button_clicked(self
, widget
):
4386 name
= self
.new_privacy_list_entry
.get_text()
4388 ErrorDialog(_('Invalid List Name'),
4389 _('You must enter a name to create a privacy list.'))
4391 key_name
= 'privacy_list_%s' % name
4392 if key_name
in gajim
.interface
.instances
[self
.account
]:
4393 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
4395 gajim
.interface
.instances
[self
.account
][key_name
] = \
4396 PrivacyListWindow(self
.account
, name
, 'NEW')
4397 self
.new_privacy_list_entry
.set_text('')
4399 def on_privacy_lists_refresh_button_clicked(self
, widget
):
4400 self
.privacy_lists_refresh()
4402 def on_open_privacy_list_button_clicked(self
, widget
):
4403 name
= self
.privacy_lists_save
[
4404 self
.list_of_privacy_lists_combobox
.get_active()]
4405 key_name
= 'privacy_list_%s' % name
4406 if key_name
in gajim
.interface
.instances
[self
.account
]:
4407 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
4409 gajim
.interface
.instances
[self
.account
][key_name
] = \
4410 PrivacyListWindow(self
.account
, name
, 'EDIT')
4412 class InvitationReceivedDialog
:
4413 def __init__(self
, account
, room_jid
, contact_jid
, password
=None,
4414 comment
=None, is_continued
=False):
4416 self
.room_jid
= room_jid
4417 self
.account
= account
4418 self
.password
= password
4419 self
.is_continued
= is_continued
4421 pritext
= _('''You are invited to a groupchat''')
4422 #Don't translate $Contact
4424 sectext
= _('$Contact has invited you to join a discussion')
4426 sectext
= _('$Contact has invited you to group chat %(room_jid)s')\
4427 % {'room_jid': room_jid
}
4428 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, contact_jid
)
4429 contact_text
= contact
and contact
.name
or contact_jid
4430 sectext
= sectext
.replace('$Contact', contact_text
)
4432 if comment
: # only if not None and not ''
4433 comment
= gobject
.markup_escape_text(comment
)
4434 comment
= _('Comment: %s') % comment
4435 sectext
+= '\n\n%s' % comment
4436 sectext
+= '\n\n' + _('Do you want to accept the invitation?')
4438 def on_yes(checked
):
4440 if self
.is_continued
:
4441 gajim
.interface
.join_gc_room(self
.account
, self
.room_jid
,
4442 gajim
.nicks
[self
.account
], None, is_continued
=True)
4444 JoinGroupchatWindow(self
.account
, self
.room_jid
)
4445 except GajimGeneralException
:
4448 YesNoDialog(pritext
, sectext
, on_response_yes
=on_yes
)
4450 class ProgressDialog
:
4451 def __init__(self
, title_text
, during_text
, messages_queue
):
4453 During text is what to show during the procedure, messages_queue has the
4454 message to show in the textview
4456 self
.xml
= gtkgui_helpers
.get_gtk_builder('progress_dialog.ui')
4457 self
.dialog
= self
.xml
.get_object('progress_dialog')
4458 self
.label
= self
.xml
.get_object('label')
4459 self
.label
.set_markup('<big>' + during_text
+ '</big>')
4460 self
.progressbar
= self
.xml
.get_object('progressbar')
4461 self
.dialog
.set_title(title_text
)
4462 self
.dialog
.set_default_size(450, 250)
4463 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
4464 self
.dialog
.show_all()
4465 self
.xml
.connect_signals(self
)
4467 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
4468 self
.update_progressbar
)
4470 def update_progressbar(self
):
4472 self
.progressbar
.pulse()
4473 return True # loop forever
4476 def on_progress_dialog_delete_event(self
, widget
, event
):
4477 return True # WM's X button or Escape key should not destroy the window
4480 class ClientCertChooserDialog(FileChooserDialog
):
4481 def __init__(self
, path_to_clientcert_file
='', on_response_ok
=None,
4482 on_response_cancel
=None):
4484 optionally accepts path_to_clientcert_file so it has that as selected
4486 def on_ok(widget
, callback
):
4488 check if file exists and call callback
4490 path_to_clientcert_file
= self
.get_filename()
4491 path_to_clientcert_file
= \
4492 gtkgui_helpers
.decode_filechooser_file_paths(
4493 (path_to_clientcert_file
,))[0]
4494 if os
.path
.exists(path_to_clientcert_file
):
4495 callback(widget
, path_to_clientcert_file
)
4497 FileChooserDialog
.__init
__(self
,
4498 title_text
=_('Choose Client Cert #PCKS12'),
4499 action
=gtk
.FILE_CHOOSER_ACTION_OPEN
,
4500 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4501 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4503 default_response
=gtk
.RESPONSE_OK
,
4504 on_response_ok
=(on_ok
, on_response_ok
),
4505 on_response_cancel
=on_response_cancel
)
4507 filter_
= gtk
.FileFilter()
4508 filter_
.set_name(_('All files'))
4509 filter_
.add_pattern('*')
4510 self
.add_filter(filter_
)
4512 filter_
= gtk
.FileFilter()
4513 filter_
.set_name(_('PKCS12 Files'))
4514 filter_
.add_pattern('*.p12')
4515 self
.add_filter(filter_
)
4516 self
.set_filter(filter_
)
4518 if path_to_clientcert_file
:
4519 # set_filename accept only absolute path
4520 path_to_clientcert_file
= os
.path
.abspath(path_to_clientcert_file
)
4521 self
.set_filename(path_to_clientcert_file
)
4524 class SoundChooserDialog(FileChooserDialog
):
4525 def __init__(self
, path_to_snd_file
='', on_response_ok
=None,
4526 on_response_cancel
=None):
4528 Optionally accepts path_to_snd_file so it has that as selected
4530 def on_ok(widget
, callback
):
4532 Check if file exists and call callback
4534 path_to_snd_file
= self
.get_filename()
4535 path_to_snd_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4536 (path_to_snd_file
,))[0]
4537 if os
.path
.exists(path_to_snd_file
):
4538 callback(widget
, path_to_snd_file
)
4540 FileChooserDialog
.__init
__(self
, title_text
= _('Choose Sound'),
4541 action
= gtk
.FILE_CHOOSER_ACTION_OPEN
,
4542 buttons
= (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4543 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4544 default_response
= gtk
.RESPONSE_OK
,
4545 current_folder
= gajim
.config
.get('last_sounds_dir'),
4546 on_response_ok
= (on_ok
, on_response_ok
),
4547 on_response_cancel
= on_response_cancel
)
4549 filter_
= gtk
.FileFilter()
4550 filter_
.set_name(_('All files'))
4551 filter_
.add_pattern('*')
4552 self
.add_filter(filter_
)
4554 filter_
= gtk
.FileFilter()
4555 filter_
.set_name(_('Wav Sounds'))
4556 filter_
.add_pattern('*.wav')
4557 self
.add_filter(filter_
)
4558 self
.set_filter(filter_
)
4560 path_to_snd_file
= helpers
.check_soundfile_path(path_to_snd_file
)
4561 if path_to_snd_file
:
4562 # set_filename accept only absolute path
4563 path_to_snd_file
= os
.path
.abspath(path_to_snd_file
)
4564 self
.set_filename(path_to_snd_file
)
4566 class ImageChooserDialog(FileChooserDialog
):
4567 def __init__(self
, path_to_file
='', on_response_ok
=None,
4568 on_response_cancel
=None):
4570 Optionally accepts path_to_snd_file so it has that as selected
4572 def on_ok(widget
, callback
):
4573 '''check if file exists and call callback'''
4574 path_to_file
= self
.get_filename()
4575 if not path_to_file
:
4577 path_to_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4579 if os
.path
.exists(path_to_file
):
4580 if isinstance(callback
, tuple):
4581 callback
[0](widget
, path_to_file
, *callback
[1:])
4583 callback(widget
, path_to_file
)
4587 path
= helpers
.get_my_pictures_path()
4589 path
= os
.environ
['HOME']
4592 FileChooserDialog
.__init
__(self
,
4593 title_text
= _('Choose Image'),
4594 action
= gtk
.FILE_CHOOSER_ACTION_OPEN
,
4595 buttons
= (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4596 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4597 default_response
= gtk
.RESPONSE_OK
,
4598 current_folder
= path
,
4599 on_response_ok
= (on_ok
, on_response_ok
),
4600 on_response_cancel
= on_response_cancel
)
4602 if on_response_cancel
:
4603 self
.connect('destroy', on_response_cancel
)
4605 filter_
= gtk
.FileFilter()
4606 filter_
.set_name(_('All files'))
4607 filter_
.add_pattern('*')
4608 self
.add_filter(filter_
)
4610 filter_
= gtk
.FileFilter()
4611 filter_
.set_name(_('Images'))
4612 filter_
.add_mime_type('image/png')
4613 filter_
.add_mime_type('image/jpeg')
4614 filter_
.add_mime_type('image/gif')
4615 filter_
.add_mime_type('image/tiff')
4616 filter_
.add_mime_type('image/svg+xml')
4617 filter_
.add_mime_type('image/x-xpixmap') # xpm
4618 self
.add_filter(filter_
)
4619 self
.set_filter(filter_
)
4622 self
.set_filename(path_to_file
)
4624 self
.set_use_preview_label(False)
4625 self
.set_preview_widget(gtk
.Image())
4626 self
.connect('selection-changed', self
.update_preview
)
4628 def update_preview(self
, widget
):
4629 path_to_file
= widget
.get_preview_filename()
4630 if path_to_file
is None or os
.path
.isdir(path_to_file
):
4631 # nothing to preview or directory
4632 # make sure you clean image do show nothing
4633 widget
.get_preview_widget().set_from_file(None)
4636 pixbuf
= gtk
.gdk
.pixbuf_new_from_file_at_size(path_to_file
, 100, 100)
4637 except gobject
.GError
:
4639 widget
.get_preview_widget().set_from_pixbuf(pixbuf
)
4641 class AvatarChooserDialog(ImageChooserDialog
):
4642 def __init__(self
, path_to_file
='', on_response_ok
=None,
4643 on_response_cancel
=None, on_response_clear
=None):
4644 ImageChooserDialog
.__init
__(self
, path_to_file
, on_response_ok
,
4646 button
= gtk
.Button(None, gtk
.STOCK_CLEAR
)
4647 self
.response_clear
= on_response_clear
4648 if on_response_clear
:
4649 button
.connect('clicked', self
.on_clear
)
4651 self
.action_area
.pack_start(button
)
4652 self
.action_area
.reorder_child(button
, 0)
4654 def on_clear(self
, widget
):
4655 if isinstance(self
.response_clear
, tuple):
4656 self
.response_clear
[0](widget
, *self
.response_clear
[1:])
4658 self
.response_clear(widget
)
4661 class ArchiveChooserDialog(FileChooserDialog
):
4662 def __init__(self
, on_response_ok
=None, on_response_cancel
=None):
4664 def on_ok(widget
, callback
):
4665 '''check if file exists and call callback'''
4666 path_to_file
= self
.get_filename()
4667 if not path_to_file
:
4669 path_to_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4671 if os
.path
.exists(path_to_file
):
4672 if isinstance(callback
, tuple):
4673 callback
[0](path_to_file
, *callback
[1:])
4675 callback(path_to_file
)
4678 path
= helpers
.get_documents_path()
4680 FileChooserDialog
.__init
__(self
,
4681 title_text
=_('Choose Archive'),
4682 action
=gtk
.FILE_CHOOSER_ACTION_OPEN
,
4683 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4684 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4685 default_response
=gtk
.RESPONSE_OK
,
4686 current_folder
=path
,
4687 on_response_ok
=(on_ok
, on_response_ok
),
4688 on_response_cancel
=on_response_cancel
)
4690 if on_response_cancel
:
4691 self
.connect('destroy', on_response_cancel
)
4693 filter_
= gtk
.FileFilter()
4694 filter_
.set_name(_('All files'))
4695 filter_
.add_pattern('*')
4696 self
.add_filter(filter_
)
4698 filter_
= gtk
.FileFilter()
4699 filter_
.set_name(_('Zip files'))
4700 filter_
.add_pattern('*.zip')
4702 self
.add_filter(filter_
)
4703 self
.set_filter(filter_
)
4706 class AddSpecialNotificationDialog
:
4707 def __init__(self
, jid
):
4709 jid is the jid for which we want to add special notification (sound and
4710 notification popups)
4712 self
.xml
= gtkgui_helpers
.get_gtk_builder(
4713 'add_special_notification_window.ui')
4714 self
.window
= self
.xml
.get_object('add_special_notification_window')
4715 self
.condition_combobox
= self
.xml
.get_object('condition_combobox')
4716 self
.condition_combobox
.set_active(0)
4717 self
.notification_popup_yes_no_combobox
= self
.xml
.get_object(
4718 'notification_popup_yes_no_combobox')
4719 self
.notification_popup_yes_no_combobox
.set_active(0)
4720 self
.listen_sound_combobox
= self
.xml
.get_object('listen_sound_combobox')
4721 self
.listen_sound_combobox
.set_active(0)
4724 self
.xml
.get_object('when_foo_becomes_label').set_text(
4725 _('When %s becomes:') % self
.jid
)
4727 self
.window
.set_title(_('Adding Special Notification for %s') % jid
)
4728 self
.window
.show_all()
4729 self
.xml
.connect_signals(self
)
4731 def on_cancel_button_clicked(self
, widget
):
4732 self
.window
.destroy()
4734 def on_add_special_notification_window_delete_event(self
, widget
, event
):
4735 self
.window
.destroy()
4737 def on_listen_sound_combobox_changed(self
, widget
):
4738 active
= widget
.get_active()
4739 if active
== 1: # user selected 'choose sound'
4740 def on_ok(widget
, path_to_snd_file
):
4742 #print path_to_snd_file
4744 def on_cancel(widget
):
4745 widget
.set_active(0) # go back to No Sound
4747 self
.dialog
= SoundChooserDialog(on_response_ok
=on_ok
,
4748 on_response_cancel
=on_cancel
)
4750 def on_ok_button_clicked(self
, widget
):
4751 conditions
= ('online', 'chat', 'online_and_chat',
4752 'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline')
4753 active
= self
.condition_combobox
.get_active()
4755 active_iter
= self
.listen_sound_combobox
.get_active_iter()
4756 listen_sound_model
= self
.listen_sound_combobox
.get_model()
4758 class TransformChatToMUC
:
4759 # Keep a reference on windows so garbage collector don't restroy them
4761 def __init__(self
, account
, jids
, preselected
=None):
4763 This window is used to trasform a one-to-one chat to a MUC. We do 2
4764 things: first select the server and then make a guests list
4767 self
.instances
.append(self
)
4768 self
.account
= account
4769 self
.auto_jids
= jids
4770 self
.preselected_jids
= preselected
4772 self
.xml
= gtkgui_helpers
.get_gtk_builder('chat_to_muc_window.ui')
4773 self
.window
= self
.xml
.get_object('chat_to_muc_window')
4775 for widget_to_add
in ('invite_button', 'cancel_button',
4776 'server_list_comboboxentry', 'guests_treeview',
4777 'server_and_guests_hseparator', 'server_select_label'):
4778 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
4781 self
.servers
= gtk
.ListStore(str)
4782 self
.server_list_comboboxentry
.set_model(self
.servers
)
4784 self
.server_list_comboboxentry
.set_text_column(0)
4786 # get the muc server of our server
4787 if 'jabber' in gajim
.connections
[account
].muc_jid
:
4788 server_list
.append(gajim
.connections
[account
].muc_jid
['jabber'])
4789 # add servers or recently joined groupchats
4790 recently_groupchat
= gajim
.config
.get('recently_groupchat').split()
4791 for g
in recently_groupchat
:
4792 server
= gajim
.get_server_from_jid(g
)
4793 if server
not in server_list
and not server
.startswith('irc'):
4794 server_list
.append(server
)
4795 # add a default server
4797 server_list
.append('conference.jabber.org')
4799 for s
in server_list
:
4800 self
.servers
.append([s
])
4802 self
.server_list_comboboxentry
.set_active(0)
4806 self
.store
= gtk
.ListStore(gtk
.gdk
.Pixbuf
, str, str)
4807 self
.store
.set_sort_column_id(1, gtk
.SORT_ASCENDING
)
4808 self
.guests_treeview
.set_model(self
.store
)
4810 renderer1
= gtk
.CellRendererText()
4811 renderer2
= gtk
.CellRendererPixbuf()
4812 column
= gtk
.TreeViewColumn('Status', renderer2
, pixbuf
=0)
4813 self
.guests_treeview
.append_column(column
)
4814 column
= gtk
.TreeViewColumn('Name', renderer1
, text
=1)
4815 self
.guests_treeview
.append_column(column
)
4817 self
.guests_treeview
.get_selection().set_mode(gtk
.SELECTION_MULTIPLE
)
4819 # All contacts beside the following can be invited:
4820 # transports, zeroconf contacts, minimized groupchats
4821 def invitable(contact
, contact_transport
=None):
4822 return (contact
.jid
not in self
.auto_jids
and
4823 contact
.jid
!= gajim
.get_jid_from_account(self
.account
) and
4824 contact
.jid
not in gajim
.interface
.minimized_controls
[account
] and
4825 not contact
.is_transport() and
4826 not contact_transport
)
4828 # set jabber id and pseudos
4829 for account
in gajim
.contacts
.get_accounts():
4830 if gajim
.connections
[account
].is_zeroconf
:
4832 for jid
in gajim
.contacts
.get_jid_list(account
):
4833 contact
= gajim
.contacts
.get_contact_with_highest_priority(
4835 contact_transport
= gajim
.get_transport_name_from_jid(jid
)
4836 # Add contact if it can be invited
4837 if invitable(contact
, contact_transport
) and \
4838 contact
.show
not in ('offline', 'error'):
4839 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
4842 name
= jid
.split('@')[0]
4843 iter_
= self
.store
.append([img
.get_pixbuf(), name
, jid
])
4844 # preselect treeview rows
4845 if self
.preselected_jids
and jid
in self
.preselected_jids
:
4846 path
= self
.store
.get_path(iter_
)
4847 self
.guests_treeview
.get_selection().select_path(path
)
4849 gajim
.ged
.register_event_handler('unique-room-id-supported', ged
.GUI1
,
4850 self
._nec
_unique
_room
_id
_supported
)
4851 gajim
.ged
.register_event_handler('unique-room-id-not-supported',
4852 ged
.GUI1
, self
._nec
_unique
_room
_id
_not
_supported
)
4855 self
.window
.show_all()
4857 self
.xml
.connect_signals(self
)
4859 def on_chat_to_muc_window_destroy(self
, widget
):
4860 gajim
.ged
.remove_event_handler('unique-room-id-supported', ged
.GUI1
,
4861 self
._nec
_unique
_room
_id
_supported
)
4862 gajim
.ged
.remove_event_handler('unique-room-id-not-supported', ged
.GUI1
,
4863 self
._nec
_unique
_room
_id
_not
_supported
)
4864 self
.instances
.remove(self
)
4866 def on_chat_to_muc_window_key_press_event(self
, widget
, event
):
4867 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
4868 self
.window
.destroy()
4870 def on_invite_button_clicked(self
, widget
):
4871 server
= self
.server_list_comboboxentry
.get_active_text()
4874 gajim
.connections
[self
.account
].check_unique_room_id_support(server
, self
)
4876 def _nec_unique_room_id_supported(self
, obj
):
4877 if obj
.instance
!= self
:
4880 guests
= self
.guests_treeview
.get_selection().get_selected_rows()
4881 for guest
in guests
[1]:
4882 iter_
= self
.store
.get_iter(guest
)
4883 guest_list
.append(self
.store
[iter_
][2].decode('utf-8'))
4884 for guest
in self
.auto_jids
:
4885 guest_list
.append(guest
)
4886 room_jid
= obj
.room_id
+ '@' + obj
.server
4887 gajim
.automatic_rooms
[self
.account
][room_jid
] = {}
4888 gajim
.automatic_rooms
[self
.account
][room_jid
]['invities'] = guest_list
4889 gajim
.automatic_rooms
[self
.account
][room_jid
]['continue_tag'] = True
4890 gajim
.interface
.join_gc_room(self
.account
, room_jid
,
4891 gajim
.nicks
[self
.account
], None, is_continued
=True)
4892 self
.window
.destroy()
4894 def on_cancel_button_clicked(self
, widget
):
4895 self
.window
.destroy()
4897 def _nec_unique_room_id_not_supported(self
, obj
):
4898 if obj
.instance
!= self
:
4900 obj
.room_id
= gajim
.nicks
[self
.account
].lower().replace(' ', '') + \
4901 str(randrange(9999999))
4902 self
._nec
_unique
_room
_id
_supported
(obj
)
4904 class DataFormWindow(Dialog
):
4905 def __init__(self
, form
, on_response_ok
):
4906 self
.df_response_ok
= on_response_ok
4907 Dialog
.__init
__(self
, None, 'test', [(gtk
.STOCK_CANCEL
,
4908 gtk
.RESPONSE_REJECT
), (gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
)],
4909 on_response_ok
=self
.on_ok
)
4910 self
.set_resizable(True)
4911 gtkgui_helpers
.resize_window(self
, 600, 400)
4912 self
.dataform_widget
= dataforms_widget
.DataFormWidget()
4913 self
.dataform
= dataforms
.ExtendForm(node
=form
)
4914 self
.dataform_widget
.set_sensitive(True)
4915 self
.dataform_widget
.data_form
= self
.dataform
4916 self
.dataform_widget
.show_all()
4917 self
.vbox
.pack_start(self
.dataform_widget
)
4920 form
= self
.dataform_widget
.data_form
4921 if isinstance(self
.df_response_ok
, tuple):
4922 self
.df_response_ok
[0](form
, *self
.df_response_ok
[1:])
4924 self
.df_response_ok(form
)
4927 class ESessionInfoWindow
:
4929 Class for displaying information about a XEP-0116 encrypted session
4931 def __init__(self
, session
):
4932 self
.session
= session
4934 self
.xml
= gtkgui_helpers
.get_gtk_builder('esession_info_window.ui')
4935 self
.xml
.connect_signals(self
)
4937 self
.security_image
= self
.xml
.get_object('security_image')
4938 self
.verify_now_button
= self
.xml
.get_object('verify_now_button')
4939 self
.button_label
= self
.xml
.get_object('button_label')
4940 self
.window
= self
.xml
.get_object('esession_info_window')
4944 self
.window
.show_all()
4946 def update_info(self
):
4947 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
}
4949 if self
.session
.verified_identity
:
4950 labeltext
+= '\n\n' + _('''You have already verified this contact's identity.''')
4951 security_image
= 'gajim-security_high'
4952 if self
.session
.control
:
4953 self
.session
.control
._show
_lock
_image
(True, 'E2E', True,
4954 self
.session
.is_loggable(), True)
4956 verification_status
= _('''Contact's identity verified''')
4957 self
.window
.set_title(verification_status
)
4958 self
.xml
.get_object('verification_status_label').set_markup(
4959 '<b><span size="x-large">%s</span></b>' % verification_status
)
4961 self
.xml
.get_object('dialog-action_area1').set_no_show_all(True)
4962 self
.button_label
.set_text(_('Verify again...'))
4964 if self
.session
.control
:
4965 self
.session
.control
._show
_lock
_image
(True, 'E2E', True,
4966 self
.session
.is_loggable(), False)
4967 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.''')
4968 security_image
= 'gajim-security_low'
4970 verification_status
= _('''Contact's identity NOT verified''')
4971 self
.window
.set_title(verification_status
)
4972 self
.xml
.get_object('verification_status_label').set_markup(
4973 '<b><span size="x-large">%s</span></b>' % verification_status
)
4975 self
.button_label
.set_text(_('Verify...'))
4977 path
= gtkgui_helpers
.get_icon_path(security_image
, 32)
4978 self
.security_image
.set_from_file(path
)
4980 self
.xml
.get_object('info_display').set_markup(labeltext
)
4982 def on_close_button_clicked(self
, widget
):
4983 self
.window
.destroy()
4985 def on_verify_now_button_clicked(self
, widget
):
4986 pritext
= _('''Have you verified the contact's identity?''')
4987 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
}
4988 sectext
+= '\n\n' + _('Did you talk to the remote contact and verify the SAS?')
4990 def on_yes(checked
):
4991 self
.session
._verified
_srs
_cb
()
4992 self
.session
.verified_identity
= True
4996 self
.session
._unverified
_srs
_cb
()
4997 self
.session
.verified_identity
= False
5000 YesNoDialog(pritext
, sectext
, on_response_yes
=on_yes
, on_response_no
=on_no
)
5002 class GPGInfoWindow
:
5004 Class for displaying information about a XEP-0116 encrypted session
5006 def __init__(self
, control
):
5007 xml
= gtkgui_helpers
.get_gtk_builder('esession_info_window.ui')
5008 security_image
= xml
.get_object('security_image')
5009 status_label
= xml
.get_object('verification_status_label')
5010 info_label
= xml
.get_object('info_display')
5011 verify_now_button
= xml
.get_object('verify_now_button')
5012 self
.window
= xml
.get_object('esession_info_window')
5013 account
= control
.account
5014 keyID
= control
.contact
.keyID
5017 verify_now_button
.set_no_show_all(True)
5018 verify_now_button
.hide()
5020 if keyID
.endswith('MISMATCH'):
5021 verification_status
= _('''Contact's identity NOT verified''')
5022 info
= _('The contact\'s key (%s) <b>does not match</b> the key '
5023 'assigned in Gajim.') % keyID
[:8]
5024 image
= 'gajim-security_low'
5026 # No key assigned nor a key is used by remote contact
5027 verification_status
= _('No GPG key assigned')
5028 info
= _('No GPG key is assigned to this contact. So you cannot '
5029 'encrypt messages.')
5030 image
= 'gajim-security_low'
5032 error
= gajim
.connections
[account
].gpg
.encrypt('test', [keyID
])[1]
5034 verification_status
= _('''Contact's identity NOT verified''')
5035 info
= _('GPG key is assigned to this contact, but <b>you do not '
5036 'trust his key</b>, so message <b>cannot</b> be encrypted. Use '
5037 'your GPG client to trust this key.')
5038 image
= 'gajim-security_low'
5040 verification_status
= _('''Contact's identity verified''')
5041 info
= _('GPG Key is assigned to this contact, and you trust his '
5042 'key, so messages will be encrypted.')
5043 image
= 'gajim-security_high'
5045 status_label
.set_markup('<b><span size="x-large">%s</span></b>' % \
5046 verification_status
)
5047 info_label
.set_markup(info
)
5049 path
= gtkgui_helpers
.get_icon_path(image
, 32)
5050 security_image
.set_from_file(path
)
5052 xml
.connect_signals(self
)
5053 self
.window
.show_all()
5055 def on_close_button_clicked(self
, widget
):
5056 self
.window
.destroy()
5060 class ResourceConflictDialog(TimeoutDialog
, InputDialog
):
5061 def __init__(self
, title
, text
, resource
, ok_handler
):
5062 TimeoutDialog
.__init
__(self
, 15, self
.on_timeout
)
5063 InputDialog
.__init
__(self
, title
, text
, input_str
=resource
,
5064 is_modal
=False, ok_handler
=ok_handler
)
5065 self
.title_text
= title
5068 def on_timeout(self
):
5069 self
.on_okbutton_clicked(None)
5073 class VoIPCallReceivedDialog(object):
5075 def __init__(self
, account
, contact_jid
, sid
, content_types
):
5076 self
.instances
[(contact_jid
, sid
)] = self
5077 self
.account
= account
5078 self
.fjid
= contact_jid
5080 self
.content_types
= content_types
5082 xml
= gtkgui_helpers
.get_gtk_builder('voip_call_received_dialog.ui')
5083 xml
.connect_signals(self
)
5085 jid
= gajim
.get_jid_without_resource(self
.fjid
)
5086 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
5087 if contact
and contact
.name
:
5088 self
.contact_text
= '%s (%s)' % (contact
.name
, jid
)
5090 self
.contact_text
= contact_jid
5092 self
.dialog
= xml
.get_object('voip_call_received_messagedialog')
5093 self
.set_secondary_text()
5095 self
.dialog
.show_all()
5098 def get_dialog(cls
, jid
, sid
):
5099 if (jid
, sid
) in cls
.instances
:
5100 return cls
.instances
[(jid
, sid
)]
5104 def set_secondary_text(self
):
5105 if 'audio' in self
.content_types
and 'video' in self
.content_types
:
5106 types_text
= _('an audio and video')
5107 elif 'audio' in self
.content_types
:
5108 types_text
= _('an audio')
5109 elif 'video' in self
.content_types
:
5110 types_text
= _('a video')
5112 # do the substitution
5113 self
.dialog
.set_property('secondary-text',
5114 _('%(contact)s wants to start %(type)s session with you. Do you want '
5115 'to answer the call?') % {'contact': self
.contact_text
,
5116 'type': types_text
})
5118 def add_contents(self
, content_types
):
5119 for type_
in content_types
:
5120 if type_
not in self
.content_types
:
5121 self
.content_types
.add(type_
)
5122 self
.set_secondary_text()
5124 def remove_contents(self
, content_types
):
5125 for type_
in content_types
:
5126 if type_
in self
.content_types
:
5127 self
.content_types
.remove(type_
)
5128 if not self
.content_types
:
5129 self
.dialog
.destroy()
5131 self
.set_secondary_text()
5133 def on_voip_call_received_messagedialog_destroy(self
, dialog
):
5134 if (self
.fjid
, self
.sid
) in self
.instances
:
5135 del self
.instances
[(self
.fjid
, self
.sid
)]
5137 def on_voip_call_received_messagedialog_close(self
, dialog
):
5138 return self
.on_voip_call_received_messagedialog_response(dialog
,
5141 def on_voip_call_received_messagedialog_response(self
, dialog
, response
):
5142 # we've got response from user, either stop connecting or accept the call
5143 session
= gajim
.connections
[self
.account
].get_jingle_session(self
.fjid
,
5147 if response
== gtk
.RESPONSE_YES
:
5148 #TODO: Ensure that ctrl.contact.resource == resource
5149 jid
= gajim
.get_jid_without_resource(self
.fjid
)
5150 resource
= gajim
.get_resource_from_jid(self
.fjid
)
5151 ctrl
= (gajim
.interface
.msg_win_mgr
.get_control(self
.fjid
, self
.account
)
5152 or gajim
.interface
.msg_win_mgr
.get_control(jid
, self
.account
)
5153 or gajim
.interface
.new_chat_from_jid(self
.account
, jid
))
5155 # Chat control opened, update content's status
5156 audio
= session
.get_content('audio')
5157 video
= session
.get_content('video')
5158 if audio
and not audio
.negotiated
:
5159 ctrl
.set_audio_state('connecting', self
.sid
)
5160 if video
and not video
.negotiated
:
5161 ctrl
.set_video_state('connecting', self
.sid
)
5162 # Now, accept the content/sessions.
5163 # This should be done after the chat control is running
5164 if not session
.accepted
:
5165 session
.approve_session()
5166 for content
in self
.content_types
:
5167 session
.approve_content(content
)
5168 else: # response==gtk.RESPONSE_NO
5169 if not session
.accepted
:
5170 session
.decline_session()
5172 for content
in self
.content_types
:
5173 session
.reject_content(content
)
5177 class CertificatDialog(InformationDialog
):
5178 def __init__(self
, parent
, account
, cert
):
5179 issuer
= cert
.get_issuer()
5180 subject
= cert
.get_subject()
5181 InformationDialog
.__init
__(self
,
5182 _('Certificate for account %s') % account
, _('''<b>Issued to:</b>
5183 Common Name (CN): %(scn)s
5184 Organization (O): %(sorg)s
5185 Organizationl Unit (OU): %(sou)s
5186 Serial Number: %(sn)s
5189 Common Name (CN): %(icn)s
5190 Organization (O): %(iorg)s
5191 Organizationl Unit (OU): %(iou)s
5198 SHA1 Fingerprint: %(sha1)s''') % {
5199 'scn': subject
.commonName
, 'sorg': subject
.organizationName
,
5200 'sou': subject
.organizationalUnitName
,
5201 'sn': cert
.get_serial_number(), 'icn': issuer
.commonName
,
5202 'iorg': issuer
.organizationName
,
5203 'iou': issuer
.organizationalUnitName
,
5204 'io': cert
.get_notBefore(), 'eo': cert
.get_notAfter(),
5205 'sha1': cert
.digest('sha1')})
5206 self
.set_transient_for(parent
)
5209 class CheckFingerprintDialog(YesNoDialog
):
5210 def __init__(self
, pritext
='', sectext
='', checktext
='',
5211 on_response_yes
=None, on_response_no
=None, account
=None, certificate
=None):
5212 self
.account
= account
5213 self
.cert
= certificate
5214 YesNoDialog
.__init
__(self
, pritext
, sectext
, checktext
, on_response_yes
,
5216 b
= gtk
.Button(_('View cert...'))
5217 b
.connect('clicked', self
.on_cert_clicked
)
5219 area
= self
.get_action_area()
5222 def on_cert_clicked(self
, button
):
5223 d
= CertificatDialog(self
, self
.account
, self
.cert
)
5225 class SSLErrorDialog(ConfirmationDialogDoubleCheck
):
5226 def __init__(self
, account
, certificate
, pritext
, sectext
, checktext1
,
5227 checktext2
, on_response_ok
=None, on_response_cancel
=None):
5228 self
.account
= account
5229 self
.cert
= certificate
5230 ConfirmationDialogDoubleCheck
.__init
__(self
, pritext
, sectext
,
5231 checktext1
, checktext2
, on_response_ok
, on_response_cancel
,
5233 b
= gtk
.Button(_('View cert...'))
5234 b
.connect('clicked', self
.on_cert_clicked
)
5236 area
= self
.get_action_area()
5239 def on_cert_clicked(self
, button
):
5240 d
= CertificatDialog(self
, self
.account
, self
.cert
)