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 HigDialog
.__init
__(self
, None,
1541 gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
,
1542 self
.on_response_ok
, self
.on_response_cancel
)
1543 self
.set_modal(False)
1545 def on_response_ok(self
, widget
):
1546 if self
.user_response_ok
:
1547 if isinstance(self
.user_response_ok
, tuple):
1548 self
.user_response_ok
[0](*self
.user_response_ok
[1:])
1550 self
.user_response_ok()
1551 self
.call_cancel_on_destroy
= False
1554 def on_response_cancel(self
, widget
):
1555 if self
.user_response_cancel
:
1556 if isinstance(self
.user_response_cancel
, tuple):
1557 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1559 self
.user_response_cancel()
1560 self
.call_cancel_on_destroy
= False
1563 class WarningDialog(HigDialog
):
1565 HIG compliant warning dialog
1568 def __init__(self
, pritext
, sectext
='', transient_for
=None):
1569 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_WARNING
, gtk
.BUTTONS_OK
,
1571 self
.set_modal(False)
1572 if transient_for
is None and hasattr(gajim
.interface
, 'roster') and \
1573 gajim
.interface
.roster
:
1574 transient_for
= gajim
.interface
.roster
.window
1576 self
.set_transient_for(transient_for
)
1579 class InformationDialog(HigDialog
):
1581 HIG compliant info dialog
1584 def __init__(self
, pritext
, sectext
=''):
1585 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_INFO
, gtk
.BUTTONS_OK
,
1587 self
.set_modal(False)
1588 self
.set_transient_for(gajim
.interface
.roster
.window
)
1591 class ErrorDialog(HigDialog
):
1593 HIG compliant error dialog
1596 def __init__(self
, pritext
, sectext
='', on_response_ok
=None,
1597 on_response_cancel
=None):
1598 HigDialog
.__init
__( self
, None, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
,
1599 pritext
, sectext
, on_response_ok
=on_response_ok
,
1600 on_response_cancel
=on_response_cancel
)
1603 class YesNoDialog(HigDialog
):
1605 HIG compliant YesNo dialog
1608 def __init__(self
, pritext
, sectext
='', checktext
='', on_response_yes
=None,
1609 on_response_no
=None):
1610 self
.user_response_yes
= on_response_yes
1611 self
.user_response_no
= on_response_no
1612 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
,
1613 pritext
, sectext
, on_response_yes
=self
.on_response_yes
,
1614 on_response_no
=self
.on_response_no
)
1617 self
.checkbutton
= gtk
.CheckButton(checktext
)
1618 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1620 self
.checkbutton
= None
1621 self
.set_modal(False)
1624 def on_response_yes(self
, widget
):
1625 if self
.user_response_yes
:
1626 if isinstance(self
.user_response_yes
, tuple):
1627 self
.user_response_yes
[0](self
.is_checked(),
1628 *self
.user_response_yes
[1:])
1630 self
.user_response_yes(self
.is_checked())
1631 self
.call_cancel_on_destroy
= False
1634 def on_response_no(self
, widget
):
1635 if self
.user_response_no
:
1636 if isinstance(self
.user_response_no
, tuple):
1637 self
.user_response_no
[0](*self
.user_response_no
[1:])
1639 self
.user_response_no()
1640 self
.call_cancel_on_destroy
= False
1643 def is_checked(self
):
1645 Get active state of the checkbutton
1647 if not self
.checkbutton
:
1649 return self
.checkbutton
.get_active()
1651 class ConfirmationDialogCheck(ConfirmationDialog
):
1653 HIG compliant confirmation dialog with checkbutton
1656 def __init__(self
, pritext
, sectext
='', checktext
='', on_response_ok
=None,
1657 on_response_cancel
=None, is_modal
=True):
1658 self
.user_response_ok
= on_response_ok
1659 self
.user_response_cancel
= on_response_cancel
1661 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_QUESTION
,
1662 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1663 self
.on_response_cancel
)
1665 self
.set_default_response(gtk
.RESPONSE_OK
)
1667 ok_button
= self
.action_area
.get_children()[0] # right to left
1668 ok_button
.grab_focus()
1670 self
.checkbutton
= gtk
.CheckButton(checktext
)
1671 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1672 self
.set_modal(is_modal
)
1675 def on_response_ok(self
, widget
):
1676 if self
.user_response_ok
:
1677 if isinstance(self
.user_response_ok
, tuple):
1678 self
.user_response_ok
[0](self
.is_checked(),
1679 *self
.user_response_ok
[1:])
1681 self
.user_response_ok(self
.is_checked())
1682 self
.call_cancel_on_destroy
= False
1685 def on_response_cancel(self
, widget
):
1686 if self
.user_response_cancel
:
1687 if isinstance(self
.user_response_cancel
, tuple):
1688 self
.user_response_cancel
[0](self
.is_checked(),
1689 *self
.user_response_cancel
[1:])
1691 self
.user_response_cancel(self
.is_checked())
1692 self
.call_cancel_on_destroy
= False
1695 def is_checked(self
):
1697 Get active state of the checkbutton
1699 return self
.checkbutton
.get_active()
1701 class ConfirmationDialogDoubleCheck(ConfirmationDialog
):
1703 HIG compliant confirmation dialog with 2 checkbuttons
1706 def __init__(self
, pritext
, sectext
='', checktext1
='', checktext2
='',
1707 on_response_ok
=None, on_response_cancel
=None, is_modal
=True):
1708 self
.user_response_ok
= on_response_ok
1709 self
.user_response_cancel
= on_response_cancel
1711 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_QUESTION
,
1712 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1713 self
.on_response_cancel
)
1715 self
.set_default_response(gtk
.RESPONSE_OK
)
1717 ok_button
= self
.action_area
.get_children()[0] # right to left
1718 ok_button
.grab_focus()
1721 self
.checkbutton1
= gtk
.CheckButton(checktext1
)
1722 self
.vbox
.pack_start(self
.checkbutton1
, expand
=False, fill
=True)
1724 self
.checkbutton1
= None
1726 self
.checkbutton2
= gtk
.CheckButton(checktext2
)
1727 self
.vbox
.pack_start(self
.checkbutton2
, expand
=False, fill
=True)
1729 self
.checkbutton2
= None
1731 self
.set_modal(is_modal
)
1734 def on_response_ok(self
, widget
):
1735 if self
.user_response_ok
:
1736 if isinstance(self
.user_response_ok
, tuple):
1737 self
.user_response_ok
[0](self
.is_checked(),
1738 *self
.user_response_ok
[1:])
1740 self
.user_response_ok(self
.is_checked())
1741 self
.call_cancel_on_destroy
= False
1744 def on_response_cancel(self
, widget
):
1745 if self
.user_response_cancel
:
1746 if isinstance(self
.user_response_cancel
, tuple):
1747 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1749 self
.user_response_cancel()
1750 self
.call_cancel_on_destroy
= False
1753 def is_checked(self
):
1754 ''' Get active state of the checkbutton '''
1755 if self
.checkbutton1
:
1756 is_checked_1
= self
.checkbutton1
.get_active()
1758 is_checked_1
= False
1759 if self
.checkbutton2
:
1760 is_checked_2
= self
.checkbutton2
.get_active()
1762 is_checked_2
= False
1763 return [is_checked_1
, is_checked_2
]
1765 class ConfirmationDialogDoubleRadio(ConfirmationDialog
):
1767 HIG compliant confirmation dialog with 2 radios
1770 def __init__(self
, pritext
, sectext
='', radiotext1
='', radiotext2
='',
1771 on_response_ok
=None, on_response_cancel
=None, is_modal
=True):
1772 self
.user_response_ok
= on_response_ok
1773 self
.user_response_cancel
= on_response_cancel
1775 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_QUESTION
,
1776 gtk
.BUTTONS_OK_CANCEL
, pritext
, sectext
, self
.on_response_ok
,
1777 self
.on_response_cancel
)
1779 self
.set_default_response(gtk
.RESPONSE_OK
)
1781 ok_button
= self
.action_area
.get_children()[0] # right to left
1782 ok_button
.grab_focus()
1784 self
.radiobutton1
= gtk
.RadioButton(label
=radiotext1
)
1785 self
.vbox
.pack_start(self
.radiobutton1
, expand
=False, fill
=True)
1787 self
.radiobutton2
= gtk
.RadioButton(group
=self
.radiobutton1
,
1789 self
.vbox
.pack_start(self
.radiobutton2
, expand
=False, fill
=True)
1791 self
.set_modal(is_modal
)
1794 def on_response_ok(self
, widget
):
1795 if self
.user_response_ok
:
1796 if isinstance(self
.user_response_ok
, tuple):
1797 self
.user_response_ok
[0](self
.is_checked(),
1798 *self
.user_response_ok
[1:])
1800 self
.user_response_ok(self
.is_checked())
1801 self
.call_cancel_on_destroy
= False
1804 def on_response_cancel(self
, widget
):
1805 if self
.user_response_cancel
:
1806 if isinstance(self
.user_response_cancel
, tuple):
1807 self
.user_response_cancel
[0](*self
.user_response_cancel
[1:])
1809 self
.user_response_cancel()
1810 self
.call_cancel_on_destroy
= False
1813 def is_checked(self
):
1814 ''' Get active state of the checkbutton '''
1815 if self
.radiobutton1
:
1816 is_checked_1
= self
.radiobutton1
.get_active()
1818 is_checked_1
= False
1819 if self
.radiobutton2
:
1820 is_checked_2
= self
.radiobutton2
.get_active()
1822 is_checked_2
= False
1823 return [is_checked_1
, is_checked_2
]
1825 class FTOverwriteConfirmationDialog(ConfirmationDialog
):
1827 HIG compliant confirmation dialog to overwrite or resume a file transfert
1830 def __init__(self
, pritext
, sectext
='', propose_resume
=True,
1832 HigDialog
.__init
__(self
, None, gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_CANCEL
,
1835 self
.on_response
= on_response
1838 b
= gtk
.Button('', gtk
.STOCK_REFRESH
)
1839 align
= b
.get_children()[0]
1840 hbox
= align
.get_children()[0]
1841 label
= hbox
.get_children()[1]
1842 label
.set_text(_('_Resume'))
1843 label
.set_use_underline(True)
1844 self
.add_action_widget(b
, 100)
1846 b
= gtk
.Button('', gtk
.STOCK_SAVE_AS
)
1847 align
= b
.get_children()[0]
1848 hbox
= align
.get_children()[0]
1849 label
= hbox
.get_children()[1]
1850 label
.set_text(_('Re_place'))
1851 label
.set_use_underline(True)
1852 self
.add_action_widget(b
, 200)
1854 self
.connect('response', self
.on_dialog_response
)
1857 def on_dialog_response(self
, dialog
, response
):
1858 if self
.on_response
:
1859 if isinstance(self
.on_response
, tuple):
1860 self
.on_response
[0](response
, *self
.on_response
[1:])
1862 self
.on_response(response
)
1863 self
.call_cancel_on_destroy
= False
1866 class CommonInputDialog
:
1868 Common Class for Input dialogs
1871 def __init__(self
, title
, label_str
, is_modal
, ok_handler
, cancel_handler
):
1872 self
.dialog
= self
.xml
.get_object('input_dialog')
1873 label
= self
.xml
.get_object('label')
1874 self
.dialog
.set_title(title
)
1875 label
.set_markup(label_str
)
1876 self
.cancel_handler
= cancel_handler
1877 self
.vbox
= self
.xml
.get_object('vbox')
1879 self
.ok_handler
= ok_handler
1880 okbutton
= self
.xml
.get_object('okbutton')
1881 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
1882 cancelbutton
= self
.xml
.get_object('cancelbutton')
1883 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
1884 self
.xml
.connect_signals(self
)
1885 self
.dialog
.show_all()
1887 def on_input_dialog_destroy(self
, widget
):
1888 if self
.cancel_handler
:
1889 self
.cancel_handler()
1891 def on_okbutton_clicked(self
, widget
):
1892 user_input
= self
.get_text()
1894 user_input
= user_input
.decode('utf-8')
1895 self
.cancel_handler
= None
1896 self
.dialog
.destroy()
1897 if isinstance(self
.ok_handler
, tuple):
1898 self
.ok_handler
[0](user_input
, *self
.ok_handler
[1:])
1900 self
.ok_handler(user_input
)
1902 def on_cancelbutton_clicked(self
, widget
):
1903 self
.dialog
.destroy()
1905 class InputDialog(CommonInputDialog
):
1907 Class for Input dialog
1910 def __init__(self
, title
, label_str
, input_str
=None, is_modal
=True,
1911 ok_handler
=None, cancel_handler
=None):
1912 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_dialog.ui')
1913 CommonInputDialog
.__init
__(self
, title
, label_str
, is_modal
, ok_handler
,
1915 self
.input_entry
= self
.xml
.get_object('input_entry')
1917 self
.set_entry(input_str
)
1919 def on_input_dialog_delete_event(self
, widget
, event
):
1921 may be implemented by subclasses
1925 def set_entry(self
, value
):
1926 self
.input_entry
.set_text(value
)
1927 self
.input_entry
.select_region(0, -1) # select all
1930 return self
.input_entry
.get_text().decode('utf-8')
1932 class InputDialogCheck(InputDialog
):
1934 Class for Input dialog
1937 def __init__(self
, title
, label_str
, checktext
='', input_str
=None,
1938 is_modal
=True, ok_handler
=None, cancel_handler
=None):
1939 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_dialog.ui')
1940 InputDialog
.__init
__(self
, title
, label_str
, input_str
=input_str
,
1941 is_modal
=is_modal
, ok_handler
=ok_handler
,
1942 cancel_handler
=cancel_handler
)
1943 self
.input_entry
= self
.xml
.get_object('input_entry')
1945 self
.input_entry
.set_text(input_str
)
1946 self
.input_entry
.select_region(0, -1) # select all
1949 self
.checkbutton
= gtk
.CheckButton(checktext
)
1950 self
.vbox
.pack_start(self
.checkbutton
, expand
=False, fill
=True)
1951 self
.checkbutton
.show()
1953 def on_okbutton_clicked(self
, widget
):
1954 user_input
= self
.get_text()
1956 user_input
= user_input
.decode('utf-8')
1957 self
.cancel_handler
= None
1958 self
.dialog
.destroy()
1959 if isinstance(self
.ok_handler
, tuple):
1960 self
.ok_handler
[0](user_input
, self
.is_checked(), *self
.ok_handler
[1:])
1962 self
.ok_handler(user_input
, self
.is_checked())
1965 return self
.input_entry
.get_text().decode('utf-8')
1967 def is_checked(self
):
1969 Get active state of the checkbutton
1972 return self
.checkbutton
.get_active()
1974 # There is no checkbutton
1977 class ChangeNickDialog(InputDialogCheck
):
1979 Class for changing room nickname in case of conflict
1982 def __init__(self
, account
, room_jid
, title
, prompt
, check_text
=None,
1985 change_nick must be set to True when we are already occupant of the room
1986 and we are changing our nick
1988 InputDialogCheck
.__init
__(self
, title
, '', checktext
=check_text
,
1989 input_str
='', is_modal
=True, ok_handler
=None, cancel_handler
=None)
1990 self
.room_queue
= [(account
, room_jid
, prompt
, change_nick
)]
1993 def on_input_dialog_delete_event(self
, widget
, event
):
1994 self
.on_cancelbutton_clicked(widget
)
1997 def setup_dialog(self
):
1998 self
.gc_control
= gajim
.interface
.msg_win_mgr
.get_gc_control(
1999 self
.room_jid
, self
.account
)
2000 if not self
.gc_control
and \
2001 self
.room_jid
in gajim
.interface
.minimized_controls
[self
.account
]:
2003 gajim
.interface
.minimized_controls
[self
.account
][self
.room_jid
]
2004 if not self
.gc_control
:
2007 label
= self
.xml
.get_object('label')
2008 label
.set_markup(self
.prompt
)
2009 self
.set_entry(self
.gc_control
.nick
+ \
2010 gajim
.config
.get('gc_proposed_nick_char'))
2012 def check_next(self
):
2013 if len(self
.room_queue
) == 0:
2014 self
.cancel_handler
= None
2015 self
.dialog
.destroy()
2016 if 'change_nick_dialog' in gajim
.interface
.instances
:
2017 del gajim
.interface
.instances
['change_nick_dialog']
2019 self
.account
, self
.room_jid
, self
.prompt
, self
.change_nick
= \
2020 self
.room_queue
.pop(0)
2023 if gajim
.new_room_nick
is not None and not gajim
.gc_connected
[
2024 self
.account
][self
.room_jid
] and self
.gc_control
.nick
!= \
2025 gajim
.new_room_nick
:
2027 self
.on_ok(gajim
.new_room_nick
, True)
2031 def on_okbutton_clicked(self
, widget
):
2032 nick
= self
.get_text()
2034 nick
= nick
.decode('utf-8')
2035 # send presence to room
2037 nick
= helpers
.parse_resource(nick
)
2040 ErrorDialog(_('Invalid nickname'),
2041 _('The nickname has not allowed characters.'))
2043 self
.on_ok(nick
, self
.is_checked())
2045 def on_ok(self
, nick
, is_checked
):
2047 gajim
.new_room_nick
= nick
2048 gajim
.connections
[self
.account
].join_gc(nick
, self
.room_jid
, None,
2049 change_nick
=self
.change_nick
)
2050 if gajim
.gc_connected
[self
.account
][self
.room_jid
]:
2051 # We are changing nick, we will change self.nick when we receive
2052 # presence that inform that it works
2053 self
.gc_control
.new_nick
= nick
2055 # We are connecting, we will not get a changed nick presence so
2056 # change it NOW. We don't already have a nick so it's harmless
2057 self
.gc_control
.nick
= nick
2060 def on_cancelbutton_clicked(self
, widget
):
2061 self
.gc_control
.new_nick
= ''
2064 def add_room(self
, account
, room_jid
, prompt
, change_nick
=False):
2065 if (account
, room_jid
, prompt
, change_nick
) not in self
.room_queue
:
2066 self
.room_queue
.append((account
, room_jid
, prompt
, change_nick
))
2068 class InputTextDialog(CommonInputDialog
):
2070 Class for multilines Input dialog (more place than InputDialog)
2073 def __init__(self
, title
, label_str
, input_str
=None, is_modal
=True,
2074 ok_handler
=None, cancel_handler
=None):
2075 self
.xml
= gtkgui_helpers
.get_gtk_builder('input_text_dialog.ui')
2076 CommonInputDialog
.__init
__(self
, title
, label_str
, is_modal
, ok_handler
,
2078 self
.input_buffer
= self
.xml
.get_object('input_textview').get_buffer()
2080 self
.input_buffer
.set_text(input_str
)
2081 start_iter
, end_iter
= self
.input_buffer
.get_bounds()
2082 self
.input_buffer
.select_range(start_iter
, end_iter
) # select all
2085 start_iter
, end_iter
= self
.input_buffer
.get_bounds()
2086 return self
.input_buffer
.get_text(start_iter
, end_iter
).decode('utf-8')
2088 class DoubleInputDialog
:
2090 Class for Double Input dialog
2093 def __init__(self
, title
, label_str1
, label_str2
, input_str1
=None,
2094 input_str2
=None, is_modal
=True, ok_handler
=None, cancel_handler
=None):
2095 self
.xml
= gtkgui_helpers
.get_gtk_builder('dubbleinput_dialog.ui')
2096 self
.dialog
= self
.xml
.get_object('dubbleinput_dialog')
2097 label1
= self
.xml
.get_object('label1')
2098 self
.input_entry1
= self
.xml
.get_object('input_entry1')
2099 label2
= self
.xml
.get_object('label2')
2100 self
.input_entry2
= self
.xml
.get_object('input_entry2')
2101 self
.dialog
.set_title(title
)
2102 label1
.set_markup(label_str1
)
2103 label2
.set_markup(label_str2
)
2104 self
.cancel_handler
= cancel_handler
2106 self
.input_entry1
.set_text(input_str1
)
2107 self
.input_entry1
.select_region(0, -1) # select all
2109 self
.input_entry2
.set_text(input_str2
)
2110 self
.input_entry2
.select_region(0, -1) # select all
2112 self
.dialog
.set_modal(is_modal
)
2114 self
.ok_handler
= ok_handler
2115 okbutton
= self
.xml
.get_object('okbutton')
2116 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
2117 cancelbutton
= self
.xml
.get_object('cancelbutton')
2118 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
2119 self
.xml
.connect_signals(self
)
2120 self
.dialog
.show_all()
2122 def on_dubbleinput_dialog_destroy(self
, widget
):
2123 if not self
.cancel_handler
:
2125 if isinstance(self
.cancel_handler
, tuple):
2126 self
.cancel_handler
[0](*self
.cancel_handler
[1:])
2128 self
.cancel_handler()
2130 def on_okbutton_clicked(self
, widget
):
2131 user_input1
= self
.input_entry1
.get_text().decode('utf-8')
2132 user_input2
= self
.input_entry2
.get_text().decode('utf-8')
2133 self
.dialog
.destroy()
2134 if not self
.ok_handler
:
2136 if isinstance(self
.ok_handler
, tuple):
2137 self
.ok_handler
[0](user_input1
, user_input2
, *self
.ok_handler
[1:])
2139 self
.ok_handler(user_input1
, user_input2
)
2141 def on_cancelbutton_clicked(self
, widget
):
2142 self
.dialog
.destroy()
2143 if not self
.cancel_handler
:
2145 if isinstance(self
.cancel_handler
, tuple):
2146 self
.cancel_handler
[0](*self
.cancel_handler
[1:])
2148 self
.cancel_handler()
2150 class SubscriptionRequestWindow
:
2151 def __init__(self
, jid
, text
, account
, user_nick
=None):
2152 xml
= gtkgui_helpers
.get_gtk_builder('subscription_request_window.ui')
2153 self
.window
= xml
.get_object('subscription_request_window')
2155 self
.account
= account
2156 self
.user_nick
= user_nick
2157 if len(gajim
.connections
) >= 2:
2159 _('Subscription request for account %(account)s from %(jid)s')\
2160 % {'account': account
, 'jid': self
.jid
}
2162 prompt_text
= _('Subscription request from %s') % self
.jid
2163 xml
.get_object('from_label').set_text(prompt_text
)
2164 xml
.get_object('message_textview').get_buffer().set_text(text
)
2165 xml
.connect_signals(self
)
2166 self
.window
.show_all()
2168 def on_subscription_request_window_destroy(self
, widget
):
2172 if self
.jid
in gajim
.interface
.instances
[self
.account
]['sub_request']:
2173 # remove us from open windows
2174 del gajim
.interface
.instances
[self
.account
]['sub_request'][self
.jid
]
2176 def prepare_popup_menu(self
):
2177 xml
= gtkgui_helpers
.get_gtk_builder('subscription_request_popup_menu.ui')
2178 menu
= xml
.get_object('subscription_request_popup_menu')
2179 xml
.connect_signals(self
)
2182 def on_close_button_clicked(self
, widget
):
2183 self
.window
.destroy()
2185 def on_authorize_button_clicked(self
, widget
):
2189 gajim
.connections
[self
.account
].send_authorization(self
.jid
)
2190 self
.window
.destroy()
2191 contact
= gajim
.contacts
.get_contact(self
.account
, self
.jid
)
2192 if not contact
or _('Not in Roster') in contact
.groups
:
2193 AddNewContactWindow(self
.account
, self
.jid
, self
.user_nick
)
2195 def on_contact_info_activate(self
, widget
):
2199 if self
.jid
in gajim
.interface
.instances
[self
.account
]['infos']:
2200 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
].window
.present()
2202 contact
= gajim
.contacts
.create_contact(jid
=self
.jid
, account
=self
.account
)
2203 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
] = \
2204 vcard
.VcardWindow(contact
, self
.account
)
2205 # Remove jabber page
2206 gajim
.interface
.instances
[self
.account
]['infos'][self
.jid
].xml
.\
2207 get_object('information_notebook').remove_page(0)
2209 def on_start_chat_activate(self
, widget
):
2213 gajim
.interface
.new_chat_from_jid(self
.account
, self
.jid
)
2215 def on_deny_button_clicked(self
, widget
):
2219 gajim
.connections
[self
.account
].refuse_authorization(self
.jid
)
2220 contact
= gajim
.contacts
.get_contact(self
.account
, self
.jid
)
2221 if contact
and _('Not in Roster') in contact
.get_shown_groups():
2222 gajim
.interface
.roster
.remove_contact(self
.jid
, self
.account
)
2223 self
.window
.destroy()
2225 def on_actions_button_clicked(self
, widget
):
2229 menu
= self
.prepare_popup_menu()
2231 gtkgui_helpers
.popup_emoticons_under_button(menu
, widget
,
2235 class JoinGroupchatWindow
:
2236 def __init__(self
, account
=None, room_jid
='', nick
='', password
='',
2239 Automatic is a dict like {'invities': []}. If automatic is not empty,
2240 this means room must be automaticaly configured and when done, invities
2241 must be automatically invited
2244 if room_jid
!= '' and room_jid
in gajim
.gc_connected
[account
] and\
2245 gajim
.gc_connected
[account
][room_jid
]:
2246 ErrorDialog(_('You are already in group chat %s') % room_jid
)
2247 raise GajimGeneralException
, 'You are already in this group chat'
2249 nick
= gajim
.nicks
[account
]
2250 if gajim
.connections
[account
].connected
< 2:
2251 ErrorDialog(_('You are not connected to the server'),
2252 _('You can not join a group chat unless you are connected.'))
2253 raise GajimGeneralException
, 'You must be connected to join a groupchat'
2255 self
.xml
= gtkgui_helpers
.get_gtk_builder('join_groupchat_window.ui')
2257 account_label
= self
.xml
.get_object('account_label')
2258 account_combobox
= self
.xml
.get_object('account_combobox')
2259 account_label
.set_no_show_all(False)
2260 account_combobox
.set_no_show_all(False)
2261 liststore
= gtk
.ListStore(str)
2262 account_combobox
.set_model(liststore
)
2263 cell
= gtk
.CellRendererText()
2264 account_combobox
.pack_start(cell
, True)
2265 account_combobox
.add_attribute(cell
, 'text', 0)
2266 account_combobox
.set_active(-1)
2268 # Add accounts, set current as active if it matches 'account'
2269 for acct
in [a
for a
in gajim
.connections
if \
2270 gajim
.account_is_connected(a
)]:
2271 if gajim
.connections
[acct
].is_zeroconf
:
2273 account_combobox
.append_text(acct
)
2274 if account
and account
== acct
:
2275 account_combobox
.set_active(liststore
.iter_n_children(None)-1)
2277 self
.account
= account
2278 self
.automatic
= automatic
2279 self
._empty
_required
_widgets
= []
2281 self
.window
= self
.xml
.get_object('join_groupchat_window')
2282 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
2283 self
._room
_jid
_entry
= self
.xml
.get_object('room_jid_entry')
2284 self
._nickname
_entry
= self
.xml
.get_object('nickname_entry')
2285 self
._password
_entry
= self
.xml
.get_object('password_entry')
2287 self
._nickname
_entry
.set_text(nick
)
2289 self
._password
_entry
.set_text(password
)
2290 self
.xml
.connect_signals(self
)
2293 # now add us to open windows
2294 gajim
.interface
.instances
[account
]['join_gc'] = self
2295 if len(gajim
.connections
) > 1:
2296 title
= _('Join Group Chat with account %s') % account
2298 title
= _('Join Group Chat')
2299 self
.window
.set_title(title
)
2301 self
.server_comboboxentry
= self
.xml
.get_object('server_comboboxentry')
2302 self
.server_model
= self
.server_comboboxentry
.get_model()
2304 # get the muc server of our server
2305 if 'jabber' in gajim
.connections
[account
].muc_jid
:
2306 server_list
.append(gajim
.connections
[account
].muc_jid
['jabber'])
2308 self
.recently_combobox
= self
.xml
.get_object('recently_combobox')
2309 liststore
= gtk
.ListStore(str)
2310 self
.recently_combobox
.set_model(liststore
)
2311 cell
= gtk
.CellRendererText()
2312 self
.recently_combobox
.pack_start(cell
, True)
2313 self
.recently_combobox
.add_attribute(cell
, 'text', 0)
2314 self
.recently_groupchat
= gajim
.config
.get('recently_groupchat').split()
2315 for g
in self
.recently_groupchat
:
2316 self
.recently_combobox
.append_text(g
)
2317 server
= gajim
.get_server_from_jid(g
)
2318 if server
not in server_list
and not server
.startswith('irc'):
2319 server_list
.append(server
)
2321 for s
in server_list
:
2322 self
.server_model
.append([s
])
2323 self
.server_comboboxentry
.set_active(0)
2325 self
._set
_room
_jid
(room_jid
)
2327 if len(self
.recently_groupchat
) == 0:
2328 self
.recently_combobox
.set_sensitive(False)
2329 elif room_jid
== '':
2330 self
.recently_combobox
.set_active(0)
2331 self
._room
_jid
_entry
.select_region(0, -1)
2332 elif room_jid
!= '':
2333 self
.xml
.get_object('join_button').grab_focus()
2335 if not self
._room
_jid
_entry
.get_text():
2336 self
._empty
_required
_widgets
.append(self
._room
_jid
_entry
)
2337 if not self
._nickname
_entry
.get_text():
2338 self
._empty
_required
_widgets
.append(self
._nickname
_entry
)
2339 if len(self
._empty
_required
_widgets
):
2340 self
.xml
.get_object('join_button').set_sensitive(False)
2342 if account
and not gajim
.connections
[account
].private_storage_supported
:
2343 self
.xml
.get_object('bookmark_checkbutton').set_sensitive(False)
2345 self
.window
.show_all()
2347 def on_join_groupchat_window_destroy(self
, widget
):
2351 if self
.account
and 'join_gc' in gajim
.interface
.instances
[self
.account
]:
2352 # remove us from open windows
2353 del gajim
.interface
.instances
[self
.account
]['join_gc']
2355 def on_join_groupchat_window_key_press_event(self
, widget
, event
):
2356 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
2359 def on_required_entry_changed(self
, widget
):
2360 if not widget
.get_text():
2361 self
._empty
_required
_widgets
.append(widget
)
2362 self
.xml
.get_object('join_button').set_sensitive(False)
2364 if widget
in self
._empty
_required
_widgets
:
2365 self
._empty
_required
_widgets
.remove(widget
)
2366 if not self
._empty
_required
_widgets
and self
.account
:
2367 self
.xml
.get_object('join_button').set_sensitive(True)
2368 text
= self
._room
_jid
_entry
.get_text()
2369 if widget
== self
._room
_jid
_entry
and '@' in text
:
2370 # Don't allow @ char in room entry
2371 room_jid
, server
= text
.split('@', 1)
2372 self
._room
_jid
_entry
.set_text(room_jid
)
2374 self
.server_comboboxentry
.child
.set_text(server
)
2375 self
.server_comboboxentry
.grab_focus()
2377 def on_account_combobox_changed(self
, widget
):
2378 model
= widget
.get_model()
2379 iter_
= widget
.get_active_iter()
2380 self
.account
= model
[iter_
][0].decode('utf-8')
2381 self
.on_required_entry_changed(self
._nickname
_entry
)
2383 def _set_room_jid(self
, room_jid
):
2384 room
, server
= gajim
.get_name_and_server_from_jid(room_jid
)
2385 self
._room
_jid
_entry
.set_text(room
)
2386 self
.server_comboboxentry
.child
.set_text(server
)
2388 def on_recently_combobox_changed(self
, widget
):
2389 model
= widget
.get_model()
2390 iter_
= widget
.get_active_iter()
2391 room_jid
= model
[iter_
][0].decode('utf-8')
2392 self
._set
_room
_jid
(room_jid
)
2394 def on_browse_rooms_button_clicked(self
, widget
):
2395 server
= self
.server_comboboxentry
.child
.get_text().decode('utf-8')
2396 if server
in gajim
.interface
.instances
[self
.account
]['disco']:
2397 gajim
.interface
.instances
[self
.account
]['disco'][server
].window
.\
2401 # Object will add itself to the window dict
2403 disco
.ServiceDiscoveryWindow(self
.account
, server
,
2404 initial_identities
=[{'category': 'conference',
2406 except GajimGeneralException
:
2409 def on_cancel_button_clicked(self
, widget
):
2411 When Cancel button is clicked
2413 self
.window
.destroy()
2415 def on_bookmark_checkbutton_toggled(self
, widget
):
2416 auto_join_checkbutton
= self
.xml
.get_object('auto_join_checkbutton')
2417 if widget
.get_active():
2418 auto_join_checkbutton
.set_sensitive(True)
2420 auto_join_checkbutton
.set_sensitive(False)
2422 def on_join_button_clicked(self
, widget
):
2424 When Join button is clicked
2426 if not self
.account
:
2427 ErrorDialog(_('Invalid Account'),
2428 _('You have to choose an account from which you want to join the '
2431 nickname
= self
._nickname
_entry
.get_text().decode('utf-8')
2432 server
= self
.server_comboboxentry
.child
.get_text().decode('utf-8')
2433 room
= self
._room
_jid
_entry
.get_text().decode('utf-8')
2434 room_jid
= room
+ '@' + server
2435 password
= self
._password
_entry
.get_text().decode('utf-8')
2437 nickname
= helpers
.parse_resource(nickname
)
2439 ErrorDialog(_('Invalid Nickname'),
2440 _('The nickname has not allowed characters.'))
2442 user
, server
, resource
= helpers
.decompose_jid(room_jid
)
2443 if not user
or not server
or resource
:
2444 ErrorDialog(_('Invalid group chat Jabber ID'),
2445 _('Please enter the group chat Jabber ID as room@server.'))
2448 room_jid
= helpers
.parse_jid(room_jid
)
2450 ErrorDialog(_('Invalid group chat Jabber ID'),
2451 _('The group chat Jabber ID has not allowed characters.'))
2454 if gajim
.contacts
.get_contact(self
.account
, room_jid
):
2455 ErrorDialog(_('This is not a group chat'),
2456 _('%s is not the name of a group chat.') % room_jid
)
2458 if room_jid
in self
.recently_groupchat
:
2459 self
.recently_groupchat
.remove(room_jid
)
2460 self
.recently_groupchat
.insert(0, room_jid
)
2461 if len(self
.recently_groupchat
) > 10:
2462 self
.recently_groupchat
= self
.recently_groupchat
[0:10]
2463 gajim
.config
.set('recently_groupchat',
2464 ' '.join(self
.recently_groupchat
))
2466 if self
.xml
.get_object('bookmark_checkbutton').get_active():
2467 if self
.xml
.get_object('auto_join_checkbutton').get_active():
2471 # Add as bookmark, with autojoin and not minimized
2472 name
= gajim
.get_nick_from_jid(room_jid
)
2473 gajim
.interface
.add_gc_bookmark(self
.account
, name
, room_jid
, autojoin
,
2474 '0', password
, nickname
)
2477 gajim
.automatic_rooms
[self
.account
][room_jid
] = self
.automatic
2479 gajim
.interface
.join_gc_room(self
.account
, room_jid
, nickname
, password
)
2481 self
.window
.destroy()
2483 class SynchroniseSelectAccountDialog
:
2484 def __init__(self
, account
):
2485 # 'account' can be None if we are about to create our first one
2486 if not account
or gajim
.connections
[account
].connected
< 2:
2487 ErrorDialog(_('You are not connected to the server'),
2488 _('Without a connection, you can not synchronise your contacts.'))
2489 raise GajimGeneralException
, 'You are not connected to the server'
2490 self
.account
= account
2491 self
.xml
= gtkgui_helpers
.get_gtk_builder('synchronise_select_account_dialog.ui')
2492 self
.dialog
= self
.xml
.get_object('synchronise_select_account_dialog')
2493 self
.accounts_treeview
= self
.xml
.get_object('accounts_treeview')
2494 model
= gtk
.ListStore(str, str, bool)
2495 self
.accounts_treeview
.set_model(model
)
2497 renderer
= gtk
.CellRendererText()
2498 self
.accounts_treeview
.insert_column_with_attributes(-1, _('Name'),
2500 renderer
= gtk
.CellRendererText()
2501 self
.accounts_treeview
.insert_column_with_attributes(-1, _('Server'),
2504 self
.xml
.connect_signals(self
)
2505 self
.init_accounts()
2506 self
.dialog
.show_all()
2508 def on_accounts_window_key_press_event(self
, widget
, event
):
2509 if event
.keyval
== gtk
.keysyms
.Escape
:
2510 self
.window
.destroy()
2512 def init_accounts(self
):
2514 Initialize listStore with existing accounts
2516 model
= self
.accounts_treeview
.get_model()
2518 for remote_account
in gajim
.connections
:
2519 if remote_account
== self
.account
:
2520 # Do not show the account we're sync'ing
2522 iter_
= model
.append()
2523 model
.set(iter_
, 0, remote_account
, 1,
2524 gajim
.get_hostname_from_account(remote_account
))
2526 def on_cancel_button_clicked(self
, widget
):
2527 self
.dialog
.destroy()
2529 def on_ok_button_clicked(self
, widget
):
2530 sel
= self
.accounts_treeview
.get_selection()
2531 (model
, iter_
) = sel
.get_selected()
2534 remote_account
= model
.get_value(iter_
, 0).decode('utf-8')
2536 if gajim
.connections
[remote_account
].connected
< 2:
2537 ErrorDialog(_('This account is not connected to the server'),
2538 _('You cannot synchronize with an account unless it is connected.'))
2542 SynchroniseSelectContactsDialog(self
.account
, remote_account
)
2543 except GajimGeneralException
:
2544 # if we showed ErrorDialog, there will not be dialog instance
2546 self
.dialog
.destroy()
2548 class SynchroniseSelectContactsDialog
:
2549 def __init__(self
, account
, remote_account
):
2550 self
.local_account
= account
2551 self
.remote_account
= remote_account
2552 self
.xml
= gtkgui_helpers
.get_gtk_builder(
2553 'synchronise_select_contacts_dialog.ui')
2554 self
.dialog
= self
.xml
.get_object('synchronise_select_contacts_dialog')
2555 self
.contacts_treeview
= self
.xml
.get_object('contacts_treeview')
2556 model
= gtk
.ListStore(bool, str)
2557 self
.contacts_treeview
.set_model(model
)
2559 renderer1
= gtk
.CellRendererToggle()
2560 renderer1
.set_property('activatable', True)
2561 renderer1
.connect('toggled', self
.toggled_callback
)
2562 self
.contacts_treeview
.insert_column_with_attributes(-1,
2563 _('Synchronise'), renderer1
, active
=0)
2564 renderer2
= gtk
.CellRendererText()
2565 self
.contacts_treeview
.insert_column_with_attributes(-1, _('Name'),
2568 self
.xml
.connect_signals(self
)
2569 self
.init_contacts()
2570 self
.dialog
.show_all()
2572 def toggled_callback(self
, cell
, path
):
2573 model
= self
.contacts_treeview
.get_model()
2574 iter_
= model
.get_iter(path
)
2575 model
[iter_
][0] = not cell
.get_active()
2577 def on_contacts_window_key_press_event(self
, widget
, event
):
2578 if event
.keyval
== gtk
.keysyms
.Escape
:
2579 self
.window
.destroy()
2581 def init_contacts(self
):
2583 Initialize listStore with existing accounts
2585 model
= self
.contacts_treeview
.get_model()
2588 # recover local contacts
2589 local_jid_list
= gajim
.contacts
.get_contacts_jid_list(self
.local_account
)
2591 remote_jid_list
= gajim
.contacts
.get_contacts_jid_list(
2592 self
.remote_account
)
2593 for remote_jid
in remote_jid_list
:
2594 if remote_jid
not in local_jid_list
:
2595 iter_
= model
.append()
2596 model
.set(iter_
, 0, True, 1, remote_jid
)
2598 def on_cancel_button_clicked(self
, widget
):
2599 self
.dialog
.destroy()
2601 def on_ok_button_clicked(self
, widget
):
2602 model
= self
.contacts_treeview
.get_model()
2603 iter_
= model
.get_iter_root()
2607 remote_jid
= model
[iter_
][1].decode('utf-8')
2608 message
= 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
2609 gajim
.get_hostname_from_account(self
.remote_account
)
2610 remote_contact
= gajim
.contacts
.get_first_contact_from_jid(
2611 self
.remote_account
, remote_jid
)
2612 # keep same groups and same nickname
2613 gajim
.interface
.roster
.req_sub(self
, remote_jid
, message
,
2614 self
.local_account
, groups
= remote_contact
.groups
,
2615 nickname
= remote_contact
.name
, auto_auth
= True)
2616 iter_
= model
.iter_next(iter_
)
2617 self
.dialog
.destroy()
2619 class NewChatDialog(InputDialog
):
2620 def __init__(self
, account
):
2621 self
.account
= account
2623 if len(gajim
.connections
) > 1:
2624 title
= _('Start Chat with account %s') % account
2626 title
= _('Start Chat')
2627 prompt_text
= _('Fill in the nickname or the Jabber ID of the contact you would like\nto send a chat message to:')
2628 InputDialog
.__init
__(self
, title
, prompt_text
, is_modal
=False)
2630 self
.completion_dict
= {}
2631 liststore
= gtkgui_helpers
.get_completion_liststore(self
.input_entry
)
2632 self
.completion_dict
= helpers
.get_contact_dict_for_account(account
)
2633 # add all contacts to the model
2634 keys
= sorted(self
.completion_dict
.keys())
2636 contact
= self
.completion_dict
[jid
]
2637 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
2638 liststore
.append((img
.get_pixbuf(), jid
))
2640 self
.ok_handler
= self
.new_chat_response
2641 okbutton
= self
.xml
.get_object('okbutton')
2642 okbutton
.connect('clicked', self
.on_okbutton_clicked
)
2643 cancelbutton
= self
.xml
.get_object('cancelbutton')
2644 cancelbutton
.connect('clicked', self
.on_cancelbutton_clicked
)
2645 self
.dialog
.set_transient_for(gajim
.interface
.roster
.window
)
2646 self
.dialog
.show_all()
2648 def new_chat_response(self
, jid
):
2650 Called when ok button is clicked
2652 if gajim
.connections
[self
.account
].connected
<= 1:
2653 #if offline or connecting
2654 ErrorDialog(_('Connection not available'),
2655 _('Please make sure you are connected with "%s".') % self
.account
)
2658 if jid
in self
.completion_dict
:
2659 jid
= self
.completion_dict
[jid
].jid
2662 jid
= helpers
.parse_jid(jid
)
2663 except helpers
.InvalidFormat
, e
:
2664 ErrorDialog(_('Invalid JID'), e
[0])
2667 ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".') % jid
)
2669 gajim
.interface
.new_chat_from_jid(self
.account
, jid
)
2671 class ChangePasswordDialog
:
2672 def __init__(self
, account
, on_response
):
2673 # 'account' can be None if we are about to create our first one
2674 if not account
or gajim
.connections
[account
].connected
< 2:
2675 ErrorDialog(_('You are not connected to the server'),
2676 _('Without a connection, you can not change your password.'))
2677 raise GajimGeneralException
, 'You are not connected to the server'
2678 self
.account
= account
2679 self
.on_response
= on_response
2680 self
.xml
= gtkgui_helpers
.get_gtk_builder('change_password_dialog.ui')
2681 self
.dialog
= self
.xml
.get_object('change_password_dialog')
2682 self
.password1_entry
= self
.xml
.get_object('password1_entry')
2683 self
.password2_entry
= self
.xml
.get_object('password2_entry')
2684 self
.dialog
.connect('response', self
.on_dialog_response
)
2686 self
.dialog
.show_all()
2688 def on_dialog_response(self
, dialog
, response
):
2689 if response
!= gtk
.RESPONSE_OK
:
2691 self
.on_response(None)
2693 password1
= self
.password1_entry
.get_text().decode('utf-8')
2695 ErrorDialog(_('Invalid password'), _('You must enter a password.'))
2697 password2
= self
.password2_entry
.get_text().decode('utf-8')
2698 if password1
!= password2
:
2699 ErrorDialog(_('Passwords do not match'),
2700 _('The passwords typed in both fields must be identical.'))
2703 self
.on_response(password1
)
2705 class PopupNotificationWindow
:
2706 def __init__(self
, event_type
, jid
, account
, msg_type
='',
2707 path_to_image
=None, title
=None, text
=None):
2708 self
.account
= account
2710 self
.msg_type
= msg_type
2712 xml
= gtkgui_helpers
.get_gtk_builder('popup_notification_window.ui')
2713 self
.window
= xml
.get_object('popup_notification_window')
2714 self
.window
.set_type_hint(gtk
.gdk
.WINDOW_TYPE_HINT_TOOLTIP
)
2715 close_button
= xml
.get_object('close_button')
2716 event_type_label
= xml
.get_object('event_type_label')
2717 event_description_label
= xml
.get_object('event_description_label')
2718 eventbox
= xml
.get_object('eventbox')
2719 image
= xml
.get_object('notification_image')
2722 text
= gajim
.get_name_from_jid(account
, jid
) # default value of text
2726 event_type_label
.set_markup(
2727 '<span foreground="black" weight="bold">%s</span>' %
2728 gobject
.markup_escape_text(title
))
2730 # set colors [ http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html ]
2731 self
.window
.modify_bg(gtk
.STATE_NORMAL
, gtk
.gdk
.color_parse('black'))
2734 if not path_to_image
:
2735 path_to_image
= gtkgui_helpers
.get_icon_path('gajim-chat_msg_recv', 48)
2737 if event_type
== _('Contact Signed In'):
2738 bg_color
= 'limegreen'
2739 elif event_type
== _('Contact Signed Out'):
2741 elif event_type
in (_('New Message'), _('New Single Message'),
2742 _('New Private Message'), _('New E-mail')):
2743 bg_color
= 'dodgerblue'
2744 elif event_type
== _('File Transfer Request'):
2746 elif event_type
== _('File Transfer Error'):
2747 bg_color
= 'firebrick'
2748 elif event_type
in (_('File Transfer Completed'),
2749 _('File Transfer Stopped')):
2750 bg_color
= 'yellowgreen'
2751 elif event_type
== _('Groupchat Invitation'):
2753 elif event_type
== _('Contact Changed Status'):
2754 bg_color
= 'thistle2'
2755 else: # Unknown event! Shouldn't happen but deal with it
2757 popup_bg_color
= gtk
.gdk
.color_parse(bg_color
)
2758 close_button
.modify_bg(gtk
.STATE_NORMAL
, popup_bg_color
)
2759 eventbox
.modify_bg(gtk
.STATE_NORMAL
, popup_bg_color
)
2760 event_description_label
.set_markup('<span foreground="black">%s</span>' %
2761 gobject
.markup_escape_text(text
))
2764 image
.set_from_file(path_to_image
)
2766 # position the window to bottom-right of screen
2767 window_width
, self
.window_height
= self
.window
.get_size()
2768 gajim
.interface
.roster
.popups_notification_height
+= self
.window_height
2769 pos_x
= gajim
.config
.get('notification_position_x')
2771 pos_x
= gtk
.gdk
.screen_width() - window_width
+ pos_x
+ 1
2772 pos_y
= gajim
.config
.get('notification_position_y')
2774 pos_y
= gtk
.gdk
.screen_height() - \
2775 gajim
.interface
.roster
.popups_notification_height
+ pos_y
+ 1
2776 self
.window
.move(pos_x
, pos_y
)
2778 xml
.connect_signals(self
)
2779 self
.window
.show_all()
2780 timeout
= gajim
.config
.get('notification_timeout')
2781 gobject
.timeout_add_seconds(timeout
, self
.on_timeout
)
2783 def on_close_button_clicked(self
, widget
):
2784 self
.adjust_height_and_move_popup_notification_windows()
2786 def on_timeout(self
):
2787 self
.adjust_height_and_move_popup_notification_windows()
2789 def adjust_height_and_move_popup_notification_windows(self
):
2791 gajim
.interface
.roster
.popups_notification_height
-= self
.window_height
2792 self
.window
.destroy()
2794 if len(gajim
.interface
.roster
.popup_notification_windows
) > 0:
2795 # we want to remove the first window added in the list
2796 gajim
.interface
.roster
.popup_notification_windows
.pop(0)
2798 # move the rest of popup windows
2799 gajim
.interface
.roster
.popups_notification_height
= 0
2800 for window_instance
in gajim
.interface
.roster
.popup_notification_windows
:
2801 window_width
, window_height
= window_instance
.window
.get_size()
2802 gajim
.interface
.roster
.popups_notification_height
+= window_height
2803 window_instance
.window
.move(gtk
.gdk
.screen_width() - window_width
,
2804 gtk
.gdk
.screen_height() - \
2805 gajim
.interface
.roster
.popups_notification_height
)
2807 def on_popup_notification_window_button_press_event(self
, widget
, event
):
2808 if event
.button
!= 1:
2809 self
.window
.destroy()
2811 gajim
.interface
.handle_event(self
.account
, self
.jid
, self
.msg_type
)
2812 self
.adjust_height_and_move_popup_notification_windows()
2814 class SingleMessageWindow
:
2816 SingleMessageWindow can send or show a received singled message depending on
2817 action argument which can be 'send' or 'receive'
2819 # Keep a reference on windows so garbage collector don't restroy them
2821 def __init__(self
, account
, to
='', action
='', from_whom
='', subject
='',
2822 message
='', resource
='', session
=None, form_node
=None):
2823 self
.instances
.append(self
)
2824 self
.account
= account
2825 self
.action
= action
2827 self
.subject
= subject
2828 self
.message
= message
2830 self
.from_whom
= from_whom
2831 self
.resource
= resource
2832 self
.session
= session
2834 self
.xml
= gtkgui_helpers
.get_gtk_builder('single_message_window.ui')
2835 self
.window
= self
.xml
.get_object('single_message_window')
2836 self
.count_chars_label
= self
.xml
.get_object('count_chars_label')
2837 self
.from_label
= self
.xml
.get_object('from_label')
2838 self
.from_entry
= self
.xml
.get_object('from_entry')
2839 self
.to_label
= self
.xml
.get_object('to_label')
2840 self
.to_entry
= self
.xml
.get_object('to_entry')
2841 self
.subject_entry
= self
.xml
.get_object('subject_entry')
2842 self
.message_scrolledwindow
= self
.xml
.get_object(
2843 'message_scrolledwindow')
2844 self
.message_textview
= self
.xml
.get_object('message_textview')
2845 self
.message_tv_buffer
= self
.message_textview
.get_buffer()
2846 self
.conversation_scrolledwindow
= self
.xml
.get_object(
2847 'conversation_scrolledwindow')
2848 self
.conversation_textview
= conversation_textview
.ConversationTextview(
2850 self
.conversation_textview
.tv
.show()
2851 self
.conversation_tv_buffer
= self
.conversation_textview
.tv
.get_buffer()
2852 self
.xml
.get_object('conversation_scrolledwindow').add(
2853 self
.conversation_textview
.tv
)
2855 self
.form_widget
= None
2856 parent_box
= self
.xml
.get_object('conversation_scrolledwindow').\
2859 dataform
= dataforms
.ExtendForm(node
=form_node
)
2860 self
.form_widget
= dataforms_widget
.DataFormWidget(dataform
)
2861 self
.form_widget
.show_all()
2862 parent_box
.add(self
.form_widget
)
2863 parent_box
.child_set_property(self
.form_widget
, 'position',
2864 parent_box
.child_get_property(self
.xml
.get_object(
2865 'conversation_scrolledwindow'), 'position'))
2866 self
.action
= 'form'
2868 self
.send_button
= self
.xml
.get_object('send_button')
2869 self
.reply_button
= self
.xml
.get_object('reply_button')
2870 self
.send_and_close_button
= self
.xml
.get_object('send_and_close_button')
2871 self
.cancel_button
= self
.xml
.get_object('cancel_button')
2872 self
.close_button
= self
.xml
.get_object('close_button')
2873 self
.message_tv_buffer
.connect('changed', self
.update_char_counter
)
2874 if isinstance(to
, list):
2875 jid
= ', '.join( [i
[0].get_full_jid() for i
in to
])
2876 self
.to_entry
.set_text(jid
)
2877 self
.to_entry
.set_sensitive(False)
2879 self
.to_entry
.set_text(to
)
2881 if gajim
.config
.get('use_speller') and HAS_GTK_SPELL
and action
== 'send':
2883 lang
= gajim
.config
.get('speller_language')
2886 gtkspell
.Spell(self
.conversation_textview
.tv
, lang
)
2887 gtkspell
.Spell(self
.message_textview
, lang
)
2888 except (gobject
.GError
, TypeError, RuntimeError, OSError):
2889 AspellDictError(lang
)
2891 self
.prepare_widgets_for(self
.action
)
2893 # set_text(None) raises TypeError exception
2894 if self
.subject
is None:
2896 self
.subject_entry
.set_text(self
.subject
)
2900 liststore
= gtkgui_helpers
.get_completion_liststore(self
.to_entry
)
2901 self
.completion_dict
= helpers
.get_contact_dict_for_account(account
)
2902 keys
= sorted(self
.completion_dict
.keys())
2904 contact
= self
.completion_dict
[jid
]
2905 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
2906 liststore
.append((img
.get_pixbuf(), jid
))
2908 self
.completion_dict
= {}
2909 self
.xml
.connect_signals(self
)
2911 # get window position and size from config
2912 gtkgui_helpers
.resize_window(self
.window
,
2913 gajim
.config
.get('single-msg-width'),
2914 gajim
.config
.get('single-msg-height'))
2915 gtkgui_helpers
.move_window(self
.window
,
2916 gajim
.config
.get('single-msg-x-position'),
2917 gajim
.config
.get('single-msg-y-position'))
2919 self
.window
.show_all()
2921 def on_single_message_window_destroy(self
, widget
):
2922 self
.instances
.remove(self
)
2923 c
= gajim
.contacts
.get_contact_with_highest_priority(self
.account
,
2926 # Groupchat is maybe already destroyed
2928 if c
.is_groupchat() and not self
.from_whom
in \
2929 gajim
.interface
.minimized_controls
[self
.account
] and self
.action
== \
2930 'receive' and gajim
.events
.get_nb_roster_events(self
.account
,
2931 self
.from_whom
, types
=['chat', 'normal']) == 0:
2932 gajim
.interface
.roster
.remove_groupchat(self
.from_whom
, self
.account
)
2934 def set_cursor_to_end(self
):
2935 end_iter
= self
.message_tv_buffer
.get_end_iter()
2936 self
.message_tv_buffer
.place_cursor(end_iter
)
2939 # save the window size and position
2940 x
, y
= self
.window
.get_position()
2941 gajim
.config
.set('single-msg-x-position', x
)
2942 gajim
.config
.set('single-msg-y-position', y
)
2943 width
, height
= self
.window
.get_size()
2944 gajim
.config
.set('single-msg-width', width
)
2945 gajim
.config
.set('single-msg-height', height
)
2946 gajim
.interface
.save_config()
2948 def on_single_message_window_delete_event(self
, window
, ev
):
2951 def prepare_widgets_for(self
, action
):
2952 if len(gajim
.connections
) > 1:
2953 if action
== 'send':
2954 title
= _('Single Message using account %s') % self
.account
2956 title
= _('Single Message in account %s') % self
.account
2958 title
= _('Single Message')
2960 if action
== 'send': # prepare UI for Sending
2961 title
= _('Send %s') % title
2962 self
.send_button
.show()
2963 self
.send_and_close_button
.show()
2964 self
.to_label
.show()
2965 self
.to_entry
.show()
2966 self
.reply_button
.hide()
2967 self
.from_label
.hide()
2968 self
.from_entry
.hide()
2969 self
.conversation_scrolledwindow
.hide()
2970 self
.message_scrolledwindow
.show()
2972 if self
.message
: # we come from a reply?
2973 self
.message_textview
.grab_focus()
2974 self
.cancel_button
.hide()
2975 self
.close_button
.show()
2976 self
.message_tv_buffer
.set_text(self
.message
)
2977 gobject
.idle_add(self
.set_cursor_to_end
)
2978 else: # we write a new message (not from reply)
2979 self
.close_button
.hide()
2980 if self
.to
: # do we already have jid?
2981 self
.subject_entry
.grab_focus()
2983 elif action
== 'receive': # prepare UI for Receiving
2984 title
= _('Received %s') % title
2985 self
.reply_button
.show()
2986 self
.from_label
.show()
2987 self
.from_entry
.show()
2988 self
.send_button
.hide()
2989 self
.send_and_close_button
.hide()
2990 self
.to_label
.hide()
2991 self
.to_entry
.hide()
2992 self
.conversation_scrolledwindow
.show()
2993 self
.message_scrolledwindow
.hide()
2996 self
.conversation_textview
.print_real_text(self
.message
)
2997 fjid
= self
.from_whom
2999 fjid
+= '/' + self
.resource
# Full jid of sender (with resource)
3000 self
.from_entry
.set_text(fjid
)
3001 self
.from_entry
.set_property('editable', False)
3002 self
.subject_entry
.set_property('editable', False)
3003 self
.reply_button
.grab_focus()
3004 self
.cancel_button
.hide()
3005 self
.close_button
.show()
3006 elif action
== 'form': # prepare UI for Receiving
3007 title
= _('Form %s') % title
3008 self
.send_button
.show()
3009 self
.send_and_close_button
.show()
3010 self
.to_label
.show()
3011 self
.to_entry
.show()
3012 self
.reply_button
.hide()
3013 self
.from_label
.hide()
3014 self
.from_entry
.hide()
3015 self
.conversation_scrolledwindow
.hide()
3016 self
.message_scrolledwindow
.hide()
3018 self
.window
.set_title(title
)
3020 def on_cancel_button_clicked(self
, widget
):
3022 self
.window
.destroy()
3024 def on_close_button_clicked(self
, widget
):
3026 self
.window
.destroy()
3028 def update_char_counter(self
, widget
):
3029 characters_no
= self
.message_tv_buffer
.get_char_count()
3030 self
.count_chars_label
.set_text(unicode(characters_no
))
3032 def send_single_message(self
):
3033 if gajim
.connections
[self
.account
].connected
<= 1:
3034 # if offline or connecting
3035 ErrorDialog(_('Connection not available'),
3036 _('Please make sure you are connected with "%s".') % self
.account
)
3038 if isinstance(self
.to
, list):
3039 sender_list
= [i
[0].jid
+ '/' + i
[0].resource
for i
in self
.to
]
3041 sender_list
= [self
.to_entry
.get_text().decode('utf-8')]
3043 for to_whom_jid
in sender_list
:
3044 if to_whom_jid
in self
.completion_dict
:
3045 to_whom_jid
= self
.completion_dict
[to_whom_jid
].jid
3047 to_whom_jid
= helpers
.parse_jid(to_whom_jid
)
3048 except helpers
.InvalidFormat
:
3049 ErrorDialog(_('Invalid Jabber ID'),
3050 _('It is not possible to send a message to %s, this JID is not '
3051 'valid.') % to_whom_jid
)
3054 subject
= self
.subject_entry
.get_text().decode('utf-8')
3055 begin
, end
= self
.message_tv_buffer
.get_bounds()
3056 message
= self
.message_tv_buffer
.get_text(begin
, end
).decode('utf-8')
3058 if '/announce/' in to_whom_jid
:
3059 gajim
.connections
[self
.account
].send_motd(to_whom_jid
, subject
,
3064 session
= self
.session
3066 session
= gajim
.connections
[self
.account
].make_new_session(
3069 if self
.form_widget
:
3070 form_node
= self
.form_widget
.data_form
3073 # FIXME: allow GPG message some day
3074 gajim
.connections
[self
.account
].send_message(to_whom_jid
, message
,
3075 keyID
=None, type_
='normal', subject
=subject
, session
=session
,
3076 form_node
=form_node
)
3078 self
.subject_entry
.set_text('') # we sent ok, clear the subject
3079 self
.message_tv_buffer
.set_text('') # we sent ok, clear the textview
3081 def on_send_button_clicked(self
, widget
):
3082 self
.send_single_message()
3084 def on_reply_button_clicked(self
, widget
):
3085 # we create a new blank window to send and we preset RE: and to jid
3086 self
.subject
= _('RE: %s') % self
.subject
3087 self
.message
= _('%s wrote:\n') % self
.from_whom
+ self
.message
3088 # add > at the begining of each line
3089 self
.message
= self
.message
.replace('\n', '\n> ') + '\n\n'
3090 self
.window
.destroy()
3091 SingleMessageWindow(self
.account
, to
=self
.from_whom
, action
='send',
3092 from_whom
=self
.from_whom
, subject
=self
.subject
, message
=self
.message
,
3093 session
=self
.session
)
3095 def on_send_and_close_button_clicked(self
, widget
):
3096 self
.send_single_message()
3098 self
.window
.destroy()
3100 def on_single_message_window_key_press_event(self
, widget
, event
):
3101 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
3103 self
.window
.destroy()
3105 class XMLConsoleWindow
:
3106 def __init__(self
, account
):
3107 self
.account
= account
3109 self
.xml
= gtkgui_helpers
.get_gtk_builder('xml_console_window.ui')
3110 self
.window
= self
.xml
.get_object('xml_console_window')
3111 self
.input_textview
= self
.xml
.get_object('input_textview')
3112 self
.stanzas_log_textview
= self
.xml
.get_object('stanzas_log_textview')
3113 self
.input_tv_buffer
= self
.input_textview
.get_buffer()
3114 buffer_
= self
.stanzas_log_textview
.get_buffer()
3115 end_iter
= buffer_
.get_end_iter()
3116 buffer_
.create_mark('end', end_iter
, False)
3118 self
.tagIn
= buffer_
.create_tag('incoming')
3119 color
= gajim
.config
.get('inmsgcolor')
3120 self
.tagIn
.set_property('foreground', color
)
3121 self
.tagInPresence
= buffer_
.create_tag('incoming_presence')
3122 self
.tagInPresence
.set_property('foreground', color
)
3123 self
.tagInMessage
= buffer_
.create_tag('incoming_message')
3124 self
.tagInMessage
.set_property('foreground', color
)
3125 self
.tagInIq
= buffer_
.create_tag('incoming_iq')
3126 self
.tagInIq
.set_property('foreground', color
)
3128 self
.tagOut
= buffer_
.create_tag('outgoing')
3129 color
= gajim
.config
.get('outmsgcolor')
3130 self
.tagOut
.set_property('foreground', color
)
3131 self
.tagOutPresence
= buffer_
.create_tag('outgoing_presence')
3132 self
.tagOutPresence
.set_property('foreground', color
)
3133 self
.tagOutMessage
= buffer_
.create_tag('outgoing_message')
3134 self
.tagOutMessage
.set_property('foreground', color
)
3135 self
.tagOutIq
= buffer_
.create_tag('outgoing_iq')
3136 self
.tagOutIq
.set_property('foreground', color
)
3137 buffer_
.create_tag('') # Default tag
3140 self
.xml
.get_object('enable_checkbutton').set_active(True)
3142 self
.input_textview
.modify_text(
3143 gtk
.STATE_NORMAL
, gtk
.gdk
.color_parse(color
))
3145 if len(gajim
.connections
) > 1:
3146 title
= _('XML Console for %s') % self
.account
3148 title
= _('XML Console')
3150 self
.window
.set_title(title
)
3151 self
.window
.show_all()
3152 gajim
.ged
.register_event_handler('stanza-received', ged
.GUI1
,
3153 self
._nec
_stanza
_received
)
3154 gajim
.ged
.register_event_handler('stanza-sent', ged
.GUI1
,
3155 self
._nec
_stanza
_sent
)
3157 self
.xml
.connect_signals(self
)
3159 def on_xml_console_window_destroy(self
, widget
):
3160 del gajim
.interface
.instances
[self
.account
]['xml_console']
3161 gajim
.ged
.remove_event_handler('stanza-received', ged
.GUI1
,
3162 self
._nec
_stanza
_received
)
3163 gajim
.ged
.remove_event_handler('stanza-sent', ged
.GUI1
,
3164 self
._nec
_stanza
_sent
)
3166 def on_clear_button_clicked(self
, widget
):
3167 buffer_
= self
.stanzas_log_textview
.get_buffer()
3168 buffer_
.set_text('')
3170 def on_enable_checkbutton_toggled(self
, widget
):
3171 self
.enabled
= widget
.get_active()
3173 def on_in_stanza_checkbutton_toggled(self
, widget
):
3174 active
= widget
.get_active()
3175 self
.tagIn
.set_property('invisible', active
)
3176 self
.tagInPresence
.set_property('invisible', active
)
3177 self
.tagInMessage
.set_property('invisible', active
)
3178 self
.tagInIq
.set_property('invisible', active
)
3180 def on_presence_stanza_checkbutton_toggled(self
, widget
):
3181 active
= widget
.get_active()
3182 self
.tagInPresence
.set_property('invisible', active
)
3183 self
.tagOutPresence
.set_property('invisible', active
)
3185 def on_out_stanza_checkbutton_toggled(self
, widget
):
3186 active
= widget
.get_active()
3187 self
.tagOut
.set_property('invisible', active
)
3188 self
.tagOutPresence
.set_property('invisible', active
)
3189 self
.tagOutMessage
.set_property('invisible', active
)
3190 self
.tagOutIq
.set_property('invisible', active
)
3192 def on_message_stanza_checkbutton_toggled(self
, widget
):
3193 active
= widget
.get_active()
3194 self
.tagInMessage
.set_property('invisible', active
)
3195 self
.tagOutMessage
.set_property('invisible', active
)
3197 def on_iq_stanza_checkbutton_toggled(self
, widget
):
3198 active
= widget
.get_active()
3199 self
.tagInIq
.set_property('invisible', active
)
3200 self
.tagOutIq
.set_property('invisible', active
)
3202 def scroll_to_end(self
, ):
3203 parent
= self
.stanzas_log_textview
.get_parent()
3204 buffer_
= self
.stanzas_log_textview
.get_buffer()
3205 end_mark
= buffer_
.get_mark('end')
3208 self
.stanzas_log_textview
.scroll_to_mark(end_mark
, 0, True, 0, 1)
3209 adjustment
= parent
.get_hadjustment()
3210 adjustment
.set_value(0)
3213 def print_stanza(self
, stanza
, kind
):
3214 # kind must be 'incoming' or 'outgoing'
3215 if not self
.enabled
:
3220 buffer = self
.stanzas_log_textview
.get_buffer()
3222 end_iter
= buffer.get_end_iter()
3223 end_rect
= self
.stanzas_log_textview
.get_iter_location(end_iter
)
3224 visible_rect
= self
.stanzas_log_textview
.get_visible_rect()
3225 if end_rect
.y
<= (visible_rect
.y
+ visible_rect
.height
):
3227 end_iter
= buffer.get_end_iter()
3230 if stanza
[1:9] == 'presence':
3232 elif stanza
[1:8] == 'message':
3234 elif stanza
[1:3] == 'iq':
3238 type_
= kind
+ '_' + type_
3240 type_
= kind
# 'incoming' or 'outgoing'
3242 if kind
== 'incoming':
3243 buffer.insert_with_tags_by_name(end_iter
, '<!-- In -->\n', type_
)
3244 elif kind
== 'outgoing':
3245 buffer.insert_with_tags_by_name(end_iter
, '<!-- Out -->\n', type_
)
3246 end_iter
= buffer.get_end_iter()
3247 buffer.insert_with_tags_by_name(end_iter
, stanza
.replace('><', '>\n<') \
3250 gobject
.idle_add(self
.scroll_to_end
)
3252 def _nec_stanza_received(self
, obj
):
3253 if obj
.conn
.name
!= self
.account
:
3255 self
.print_stanza(obj
.stanza_str
, 'incoming')
3257 def _nec_stanza_sent(self
, obj
):
3258 if obj
.conn
.name
!= self
.account
:
3260 self
.print_stanza(obj
.stanza_str
, 'outgoing')
3262 def on_send_button_clicked(self
, widget
):
3263 if gajim
.connections
[self
.account
].connected
<= 1:
3264 # if offline or connecting
3265 ErrorDialog(_('Connection not available'),
3266 _('Please make sure you are connected with "%s".') % \
3269 begin_iter
, end_iter
= self
.input_tv_buffer
.get_bounds()
3270 stanza
= self
.input_tv_buffer
.get_text(begin_iter
, end_iter
).decode(
3273 gajim
.connections
[self
.account
].send_stanza(stanza
)
3274 self
.input_tv_buffer
.set_text('') # we sent ok, clear the textview
3276 def on_presence_button_clicked(self
, widget
):
3277 self
.input_tv_buffer
.set_text(
3278 '<presence><show></show><status></status><priority></priority>'
3281 def on_iq_button_clicked(self
, widget
):
3282 self
.input_tv_buffer
.set_text(
3283 '<iq to="" type=""><query xmlns=""></query></iq>')
3285 def on_message_button_clicked(self
, widget
):
3286 self
.input_tv_buffer
.set_text(
3287 '<message to="" type=""><body></body></message>')
3289 def on_expander_activate(self
, widget
):
3290 if not widget
.get_expanded(): # it's the opposite!
3292 self
.input_textview
.grab_focus()
3294 #Action that can be done with an incoming list of contacts
3295 TRANSLATED_ACTION
= {'add': _('add'), 'modify': _('modify'),
3296 'remove': _('remove')}
3297 class RosterItemExchangeWindow
:
3299 Windows used when someone send you a exchange contact suggestion
3302 def __init__(self
, account
, action
, exchange_list
, jid_from
,
3304 self
.account
= account
3305 self
.action
= action
3306 self
.exchange_list
= exchange_list
3307 self
.message_body
= message_body
3308 self
.jid_from
= jid_from
3312 # Connect to gtk builder
3313 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3314 'roster_item_exchange_window.ui')
3315 self
.window
= self
.xml
.get_object('roster_item_exchange_window')
3318 for widget_to_add
in ['accept_button_label', 'type_label',
3319 'body_scrolledwindow', 'body_textview', 'items_list_treeview']:
3320 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3323 # self.action can be 'add', 'modify' or 'remove'
3324 self
.type_label
.set_label(
3325 _('<b>%(jid)s</b> would like you to <b>%(action)s</b> some contacts '
3326 'in your roster.') % {'jid': jid_from
,
3327 'action': TRANSLATED_ACTION
[self
.action
]})
3329 buffer_
= self
.body_textview
.get_buffer()
3330 buffer_
.set_text(self
.message_body
)
3332 self
.body_scrolledwindow
.hide()
3334 model
= gtk
.ListStore(bool, str, str, str, str)
3335 self
.items_list_treeview
.set_model(model
)
3337 renderer1
= gtk
.CellRendererToggle()
3338 renderer1
.set_property('activatable', True)
3339 renderer1
.connect('toggled', self
.toggled_callback
)
3340 if self
.action
== 'add':
3342 elif self
.action
== 'modify':
3344 elif self
.action
== 'delete':
3346 self
.items_list_treeview
.insert_column_with_attributes(-1, title
,
3347 renderer1
, active
=0)
3348 renderer2
= gtk
.CellRendererText()
3349 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Jabber ID'),
3351 renderer3
= gtk
.CellRendererText()
3352 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Name'),
3354 renderer4
= gtk
.CellRendererText()
3355 self
.items_list_treeview
.insert_column_with_attributes(-1, _('Groups'),
3359 model
= self
.items_list_treeview
.get_model()
3363 for jid
in self
.exchange_list
:
3366 contact
= gajim
.contacts
.get_contact_with_highest_priority(
3369 is_in_roster
= False
3370 name
= self
.exchange_list
[jid
][0]
3371 num_list
= len(self
.exchange_list
[jid
][1])
3373 for group
in self
.exchange_list
[jid
][1]:
3375 if contact
and not group
in contact
.groups
:
3376 is_in_roster
= False
3377 if current
== num_list
:
3378 groups
= groups
+ group
3380 groups
= groups
+ group
+ ', '
3381 if not is_in_roster
:
3383 iter_
= model
.append()
3384 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3386 # Change label for accept_button to action name instead of 'OK'.
3387 self
.accept_button_label
.set_label(_('Add'))
3388 elif action
== 'modify':
3389 for jid
in self
.exchange_list
:
3393 contact
= gajim
.contacts
.get_contact_with_highest_priority(
3395 name
= self
.exchange_list
[jid
][0]
3397 is_in_roster
= False
3400 if name
!= contact
.name
:
3402 num_list
= len(self
.exchange_list
[jid
][1])
3404 for group
in self
.exchange_list
[jid
][1]:
3406 if contact
and not group
in contact
.groups
:
3408 if current
== num_list
:
3409 groups
= groups
+ group
3411 groups
= groups
+ group
+ ', '
3412 if not is_right
and is_in_roster
:
3414 iter_
= model
.append()
3415 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3417 # Change label for accept_button to action name instead of 'OK'.
3418 self
.accept_button_label
.set_label(_('Modify'))
3419 elif action
== 'delete':
3420 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
3428 num_list
= len(self
.exchange_list
[jid
][1])
3430 for group
in self
.exchange_list
[jid
][1]:
3432 if current
== num_list
:
3433 groups
= groups
+ group
3435 groups
= groups
+ group
+ ', '
3438 iter_
= model
.append()
3439 model
.set(iter_
, 0, True, 1, jid
, 2, name
, 3, groups
)
3441 # Change label for accept_button to action name instead of 'OK'.
3442 self
.accept_button_label
.set_label(_('Delete'))
3445 self
.window
.show_all()
3446 self
.xml
.connect_signals(self
)
3448 def toggled_callback(self
, cell
, path
):
3449 model
= self
.items_list_treeview
.get_model()
3450 iter_
= model
.get_iter(path
)
3451 model
[iter_
][0] = not cell
.get_active()
3453 def on_accept_button_clicked(self
, widget
):
3454 model
= self
.items_list_treeview
.get_model()
3455 iter_
= model
.get_iter_root()
3456 if self
.action
== 'add':
3462 #remote_jid = model[iter_][1].decode('utf-8')
3463 message
= _('%s suggested me to add you in my roster.'
3465 # keep same groups and same nickname
3466 groups
= model
[iter_
][3].split(', ')
3469 jid
= model
[iter_
][1].decode('utf-8')
3470 if gajim
.jid_is_transport(self
.jid_from
):
3471 gajim
.connections
[self
.account
].automatically_added
.append(
3473 gajim
.interface
.roster
.req_sub(self
, jid
, message
,
3474 self
.account
, groups
=groups
, nickname
=model
[iter_
][2],
3476 iter_
= model
.iter_next(iter_
)
3477 InformationDialog(_('Added %s contacts') % str(a
))
3478 elif self
.action
== 'modify':
3484 jid
= model
[iter_
][1].decode('utf-8')
3485 # keep same groups and same nickname
3486 groups
= model
[iter_
][3].split(', ')
3489 for u
in gajim
.contacts
.get_contact(self
.account
, jid
):
3490 u
.name
= model
[iter_
][2]
3491 gajim
.connections
[self
.account
].update_contact(jid
,
3492 model
[iter_
][2], groups
)
3493 self
.draw_contact(jid
, self
.account
)
3494 # Update opened chat
3495 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(jid
, self
.account
)
3498 win
= gajim
.interface
.msg_win_mgr
.get_window(jid
,
3500 win
.redraw_tab(ctrl
)
3502 iter_
= model
.iter_next(iter_
)
3503 elif self
.action
== 'delete':
3509 jid
= model
[iter_
][1].decode('utf-8')
3510 gajim
.connections
[self
.account
].unsubscribe(jid
)
3511 gajim
.interface
.roster
.remove_contact(jid
, self
.account
)
3512 gajim
.contacts
.remove_jid(self
.account
, jid
)
3513 iter_
= model
.iter_next(iter_
)
3514 InformationDialog(_('Removed %s contacts') % str(a
))
3515 self
.window
.destroy()
3517 def on_cancel_button_clicked(self
, widget
):
3518 self
.window
.destroy()
3521 class ItemArchivingPreferencesWindow
:
3522 otr_name
= ('approve', 'concede', 'forbid', 'oppose', 'prefer', 'require')
3523 otr_index
= dict([(j
, i
) for i
, j
in enumerate(otr_name
)])
3524 save_name
= ('body', 'false', 'message', 'stream')
3525 save_index
= dict([(j
, i
) for i
, j
in enumerate(save_name
)])
3527 def __init__(self
, account
, item
):
3528 self
.account
= account
3530 if self
.item
and self
.item
!= 'Default':
3531 self
.item_config
= gajim
.connections
[self
.account
].items
[self
.item
]
3533 self
.item_config
= gajim
.connections
[self
.account
].default
3536 # Connect to gtk builder
3537 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3538 'item_archiving_preferences_window.ui')
3539 self
.window
= self
.xml
.get_object('item_archiving_preferences_window')
3542 for widget_to_add
in ('jid_entry', 'expire_entry', 'otr_combobox',
3543 'save_combobox', 'cancel_button', 'ok_button', 'progressbar'):
3544 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3547 self
.jid_entry
.set_text(self
.item
)
3548 expire_value
= self
.item_config
['expire'] or ''
3549 self
.otr_combobox
.set_active(self
.otr_index
[self
.item_config
['otr']])
3550 self
.save_combobox
.set_active(
3551 self
.save_index
[self
.item_config
['save']])
3552 self
.expire_entry
.set_text(expire_value
)
3554 self
.window
.set_title(_('Archiving Preferences for %s') % self
.account
)
3556 self
.window
.show_all()
3557 self
.progressbar
.hide()
3558 self
.xml
.connect_signals(self
)
3560 def update_progressbar(self
):
3562 self
.progressbar
.pulse()
3566 def on_otr_combobox_changed(self
, widget
):
3567 otr
= self
.otr_name
[self
.otr_combobox
.get_active()]
3568 if otr
== 'require':
3569 self
.save_combobox
.set_active(self
.save_index
['false'])
3571 def on_ok_button_clicked(self
, widget
):
3572 # Return directly if operation in progress
3576 item
= self
.jid_entry
.get_text()
3577 otr
= self
.otr_name
[self
.otr_combobox
.get_active()]
3578 save
= self
.save_name
[self
.save_combobox
.get_active()]
3579 expire
= self
.expire_entry
.get_text()
3581 if self
.item
!= 'Default':
3583 item
= helpers
.parse_jid(item
)
3584 except helpers
.InvalidFormat
, s
:
3585 pritext
= _('Invalid User ID')
3586 ErrorDialog(pritext
, str(s
))
3591 if int(expire
) < 0 or str(int(expire
)) != expire
:
3594 pritext
= _('Invalid expire value')
3595 sectext
= _('Expire must be a valid positive integer.')
3596 ErrorDialog(pritext
, sectext
)
3599 if not (item
== self
.item
and expire
== self
.item_config
['expire'] and
3600 otr
== self
.item_config
['otr'] and save
== self
.item_config
['save']):
3601 if not self
.item
or self
.item
== item
:
3602 if self
.item
== 'Default':
3603 self
.waiting
= 'default'
3604 gajim
.connections
[self
.account
].set_default(
3607 self
.waiting
= 'item'
3608 gajim
.connections
[self
.account
].append_or_update_item(
3609 item
, otr
, save
, expire
)
3611 self
.waiting
= 'item'
3612 gajim
.connections
[self
.account
].append_or_update_item(
3613 item
, otr
, save
, expire
)
3614 gajim
.connections
[self
.account
].remove_item(self
.item
)
3615 self
.launch_progressbar()
3616 #self.window.destroy()
3618 def on_cancel_button_clicked(self
, widget
):
3619 self
.window
.destroy()
3621 def on_item_archiving_preferences_window_destroy(self
, widget
):
3623 key_name
= 'edit_item_archiving_preferences_%s' % self
.item
3625 key_name
= 'new_item_archiving_preferences'
3626 if key_name
in gajim
.interface
.instances
[self
.account
]:
3627 del gajim
.interface
.instances
[self
.account
][key_name
]
3629 def launch_progressbar(self
):
3630 self
.progressbar
.show()
3631 self
.update_progressbar_timeout_id
= gobject
.timeout_add(
3632 100, self
.update_progressbar
)
3634 def response_arrived(self
, data
):
3636 self
.window
.destroy()
3638 def error_arrived(self
, error
):
3641 self
.progressbar
.hide()
3642 pritext
= _('There is an error with the form')
3644 ErrorDialog(pritext
, sectext
)
3647 class ArchivingPreferencesWindow
:
3648 auto_name
= ('false', 'true')
3649 auto_index
= dict([(j
, i
) for i
, j
in enumerate(auto_name
)])
3650 method_foo_name
= ('prefer', 'concede', 'forbid')
3651 method_foo_index
= dict([(j
, i
) for i
, j
in enumerate(method_foo_name
)])
3653 def __init__(self
, account
):
3654 self
.account
= account
3658 self
.xml
= gtkgui_helpers
.get_gtk_builder(
3659 'archiving_preferences_window.ui')
3660 self
.window
= self
.xml
.get_object('archiving_preferences_window')
3663 for widget_to_add
in ('auto_combobox', 'method_auto_combobox',
3664 'method_local_combobox', 'method_manual_combobox', 'close_button',
3665 'item_treeview', 'item_notebook', 'otr_combobox', 'save_combobox',
3666 'expire_entry', 'remove_button', 'edit_button'):
3667 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3669 self
.auto_combobox
.set_active(
3670 self
.auto_index
[gajim
.connections
[self
.account
].auto
])
3671 self
.method_auto_combobox
.set_active(
3672 self
.method_foo_index
[gajim
.connections
[self
.account
].method_auto
])
3673 self
.method_local_combobox
.set_active(
3674 self
.method_foo_index
[gajim
.connections
[self
.account
].method_local
])
3675 self
.method_manual_combobox
.set_active(
3676 self
.method_foo_index
[gajim
.connections
[self
.account
].\
3679 model
= gtk
.ListStore(str, str, str, str)
3680 self
.item_treeview
.set_model(model
)
3681 col
= gtk
.TreeViewColumn('jid')
3682 self
.item_treeview
.append_column(col
)
3683 renderer
= gtk
.CellRendererText()
3684 col
.pack_start(renderer
, True)
3685 col
.set_attributes(renderer
, text
=0)
3687 col
= gtk
.TreeViewColumn('expire')
3688 col
.pack_start(renderer
, True)
3689 col
.set_attributes(renderer
, text
=1)
3690 self
.item_treeview
.append_column(col
)
3692 col
= gtk
.TreeViewColumn('otr')
3693 col
.pack_start(renderer
, True)
3694 col
.set_attributes(renderer
, text
=2)
3695 self
.item_treeview
.append_column(col
)
3697 col
= gtk
.TreeViewColumn('save')
3698 col
.pack_start(renderer
, True)
3699 col
.set_attributes(renderer
, text
=3)
3700 self
.item_treeview
.append_column(col
)
3704 self
.current_item
= None
3706 def sort_items(model
, iter1
, iter2
):
3707 item1
= model
.get_value(iter1
, 0)
3708 item2
= model
.get_value(iter2
, 0)
3709 if item1
== 'Default':
3711 if item2
== 'Default':
3714 if '@' not in item2
:
3722 # item1 == item2 ? WTF?
3725 model
.set_sort_column_id(0, gtk
.SORT_ASCENDING
)
3726 model
.set_sort_func(0, sort_items
)
3728 self
.remove_button
.set_sensitive(False)
3729 self
.edit_button
.set_sensitive(False)
3731 self
.window
.set_title(_('Archiving Preferences for %s') % self
.account
)
3733 gajim
.ged
.register_event_handler(
3734 'archiving-preferences-changed-received', ged
.GUI1
,
3735 self
._nec
_archiving
_changed
_received
)
3736 gajim
.ged
.register_event_handler('archiving-error-received', ged
.GUI1
,
3737 self
._nec
_archiving
_error
)
3739 self
.window
.show_all()
3741 self
.xml
.connect_signals(self
)
3743 def on_add_item_button_clicked(self
, widget
):
3744 key_name
= 'new_item_archiving_preferences'
3745 if key_name
in gajim
.interface
.instances
[self
.account
]:
3746 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
3748 gajim
.interface
.instances
[self
.account
][key_name
] = \
3749 ItemArchivingPreferencesWindow(self
.account
, '')
3751 def on_remove_item_button_clicked(self
, widget
):
3752 if not self
.current_item
:
3755 self
.waiting
.append('itemremove')
3756 sel
= self
.item_treeview
.get_selection()
3757 (model
, iter_
) = sel
.get_selected()
3758 gajim
.connections
[self
.account
].remove_item(model
[iter_
][0])
3760 self
.remove_button
.set_sensitive(False)
3761 self
.edit_button
.set_sensitive(False)
3763 def on_edit_item_button_clicked(self
, widget
):
3764 if not self
.current_item
:
3767 key_name
= 'edit_item_archiving_preferences_%s' % self
.current_item
3768 if key_name
in gajim
.interface
.instances
[self
.account
]:
3769 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
3771 gajim
.interface
.instances
[self
.account
][key_name
] = \
3772 ItemArchivingPreferencesWindow(self
.account
, self
.current_item
)
3774 def on_item_treeview_cursor_changed(self
, widget
):
3775 sel
= self
.item_treeview
.get_selection()
3776 (model
, iter_
) = sel
.get_selected()
3779 item
= model
[iter_
][0]
3780 if self
.current_item
and self
.current_item
== item
:
3783 self
.current_item
= item
3784 if self
.current_item
== 'Default':
3785 self
.remove_button
.set_sensitive(False)
3786 self
.edit_button
.set_sensitive(True)
3787 elif self
.current_item
:
3788 self
.remove_button
.set_sensitive(True)
3789 self
.edit_button
.set_sensitive(True)
3791 self
.remove_button
.set_sensitive(False)
3792 self
.edit_button
.set_sensitive(False)
3794 def on_auto_combobox_changed(self
, widget
):
3795 save
= self
.auto_name
[widget
.get_active()]
3796 gajim
.connections
[self
.account
].set_auto(save
)
3798 def on_method_foo_combobox_changed(self
, widget
):
3799 # We retrieve method type from widget name
3800 # ('foo' in 'method_foo_combobox')
3801 method_type
= widget
.name
.split('_')[1]
3802 use
= self
.method_foo_name
[widget
.get_active()]
3803 self
.waiting
.append('method_%s' % method_type
)
3804 gajim
.connections
[self
.account
].set_method(method_type
, use
)
3806 def get_child_window(self
):
3807 edit_key_name
= 'edit_item_archiving_preferences_%s' % self
.current_item
3808 new_key_name
= 'new_item_archiving_preferences'
3810 if edit_key_name
in gajim
.interface
.instances
[self
.account
]:
3811 return gajim
.interface
.instances
[self
.account
][edit_key_name
]
3813 if new_key_name
in gajim
.interface
.instances
[self
.account
]:
3814 return gajim
.interface
.instances
[self
.account
][new_key_name
]
3816 def _nec_archiving_changed_received(self
, obj
):
3817 if obj
.conn
.name
!= self
.account
:
3819 for key
in ('auto', 'method_auto', 'method_local', 'method_manual'):
3820 if key
in obj
.conf
and key
in self
.waiting
:
3821 self
.waiting
.remove(key
)
3822 if 'default' in obj
.conf
:
3823 key_name
= 'edit_item_archiving_preferences_%s' % \
3825 if key_name
in gajim
.interface
.instances
[self
.account
]:
3826 gajim
.interface
.instances
[self
.account
][key_name
].\
3827 response_arrived(obj
.conf
['default'])
3828 self
.fill_items(True)
3829 for jid
, pref
in obj
.new_items
.items():
3830 child
= self
.get_child_window()
3832 is_new
= not child
.item
3833 child
.response_arrived(pref
)
3835 model
= self
.item_treeview
.get_model()
3836 model
.append((jid
, pref
['expire'], pref
['otr'],
3839 self
.fill_items(True)
3840 if 'itemremove' in self
.waiting
and obj
.removed_items
:
3841 self
.waiting
.remove('itemremove')
3842 self
.fill_items(True)
3844 def fill_items(self
, clear
=False):
3845 model
= self
.item_treeview
.get_model()
3848 default_config
= gajim
.connections
[self
.account
].default
3849 expire_value
= default_config
['expire'] or ''
3850 model
.append(('Default', expire_value
,
3851 default_config
['otr'], default_config
['save']))
3852 for item
, item_config
in \
3853 gajim
.connections
[self
.account
].items
.items():
3854 expire_value
= item_config
['expire'] or ''
3855 model
.append((item
, expire_value
, item_config
['otr'],
3856 item_config
['save']))
3858 def _nec_archiving_error(self
, obj
):
3859 if obj
.conn
.name
!= self
.account
:
3862 pritext
= _('There is an error')
3863 sectext
= obj
.error_msg
3864 ErrorDialog(pritext
, sectext
)
3867 child
= self
.get_child_window()
3869 child
.error_arrived(obj
.error_msg
)
3871 def on_close_button_clicked(self
, widget
):
3872 self
.window
.destroy()
3874 def on_archiving_preferences_window_destroy(self
, widget
):
3875 gajim
.ged
.remove_event_handler(
3876 'archiving-preferences-changed-received', ged
.GUI1
,
3877 self
._nec
_archiving
_changed
_received
)
3878 gajim
.ged
.remove_event_handler('archiving-error-received', ged
.GUI1
,
3879 self
._nec
_archiving
_error
)
3880 if 'archiving_preferences' in gajim
.interface
.instances
[self
.account
]:
3881 del gajim
.interface
.instances
[self
.account
]['archiving_preferences']
3884 class PrivacyListWindow
:
3886 Window that is used for creating NEW or EDITING already there privacy lists
3889 def __init__(self
, account
, privacy_list_name
, action
):
3890 '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
3891 or edit an already existing one'''
3892 self
.account
= account
3893 self
.privacy_list_name
= privacy_list_name
3895 # Dicts and Default Values
3896 self
.active_rule
= ''
3897 self
.global_rules
= {}
3898 self
.list_of_groups
= {}
3902 # Default Edit Values
3903 self
.edit_rule_type
= 'jid'
3904 self
.allow_deny
= 'allow'
3906 # Connect to gtk builder
3907 self
.xml
= gtkgui_helpers
.get_gtk_builder('privacy_list_window.ui')
3908 self
.window
= self
.xml
.get_object('privacy_list_edit_window')
3912 for widget_to_add
in ('title_hbox', 'privacy_lists_title_label',
3913 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
3914 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
3915 'list_of_rules_combobox', 'delete_open_buttons_hbox',
3916 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
3917 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
3918 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
3919 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
3920 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
3921 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
3922 'edit_view_status_checkbutton', 'edit_all_checkbutton',
3923 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
3924 'privacy_list_refresh_button', 'privacy_list_close_button',
3925 'edit_send_status_checkbutton', 'add_edit_vbox',
3926 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
3927 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
3929 self
.privacy_lists_title_label
.set_label(
3930 _('Privacy List <b><i>%s</i></b>') % \
3931 gobject
.markup_escape_text(self
.privacy_list_name
))
3933 if len(gajim
.connections
) > 1:
3934 title
= _('Privacy List for %s') % self
.account
3936 title
= _('Privacy List')
3938 self
.delete_rule_button
.set_sensitive(False)
3939 self
.open_rule_button
.set_sensitive(False)
3940 self
.privacy_list_active_checkbutton
.set_sensitive(False)
3941 self
.privacy_list_default_checkbutton
.set_sensitive(False)
3942 self
.list_of_rules_combobox
.set_sensitive(False)
3944 # set jabber id completion
3945 jids_list_store
= gtk
.ListStore(gobject
.TYPE_STRING
)
3946 for jid
in gajim
.contacts
.get_jid_list(self
.account
):
3947 jids_list_store
.append([jid
])
3948 jid_entry_completion
= gtk
.EntryCompletion()
3949 jid_entry_completion
.set_text_column(0)
3950 jid_entry_completion
.set_model(jids_list_store
)
3951 jid_entry_completion
.set_popup_completion(True)
3952 self
.edit_type_jabberid_entry
.set_completion(jid_entry_completion
)
3953 if action
== 'EDIT':
3954 self
.refresh_rules()
3957 for group
in gajim
.groups
[self
.account
]:
3958 self
.list_of_groups
[group
] = count
3960 self
.edit_type_group_combobox
.append_text(group
)
3961 self
.edit_type_group_combobox
.set_active(0)
3963 self
.window
.set_title(title
)
3965 gajim
.ged
.register_event_handler('privacy-list-received', ged
.GUI1
,
3966 self
._nec
_privacy
_list
_received
)
3967 gajim
.ged
.register_event_handler('privacy-list-active-default',
3968 ged
.GUI1
, self
._nec
_privacy
_list
_active
_default
)
3970 self
.window
.show_all()
3971 self
.add_edit_vbox
.hide()
3973 self
.xml
.connect_signals(self
)
3975 def on_privacy_list_edit_window_destroy(self
, widget
):
3976 key_name
= 'privacy_list_%s' % self
.privacy_list_name
3977 if key_name
in gajim
.interface
.instances
[self
.account
]:
3978 del gajim
.interface
.instances
[self
.account
][key_name
]
3979 gajim
.ged
.remove_event_handler('privacy-list-received', ged
.GUI1
,
3980 self
._nec
_privacy
_list
_received
)
3981 gajim
.ged
.remove_event_handler('privacy-list-active-default',
3982 ged
.GUI1
, self
._nec
_privacy
_list
_active
_default
)
3984 def _nec_privacy_list_active_default(self
, obj
):
3985 if obj
.conn
.name
!= self
.account
:
3987 if obj
.active_list
== self
.privacy_list_name
:
3988 self
.privacy_list_active_checkbutton
.set_active(True)
3990 self
.privacy_list_active_checkbutton
.set_active(False)
3991 if obj
.default_list
== self
.privacy_list_name
:
3992 self
.privacy_list_default_checkbutton
.set_active(True)
3994 self
.privacy_list_default_checkbutton
.set_active(False)
3996 def privacy_list_received(self
, rules
):
3997 self
.list_of_rules_combobox
.get_model().clear()
3998 self
.global_rules
= {}
4001 text_item
= _('Order: %(order)s, action: %(action)s, type: %(type)s'
4002 ', value: %(value)s') % {'order': rule
['order'],
4003 'action': rule
['action'], 'type': rule
['type'],
4004 'value': rule
['value']}
4006 text_item
= _('Order: %(order)s, action: %(action)s') % \
4007 {'order': rule
['order'], 'action': rule
['action']}
4008 if int(rule
['order']) > self
.max_order
:
4009 self
.max_order
= int(rule
['order'])
4010 self
.global_rules
[text_item
] = rule
4011 self
.list_of_rules_combobox
.append_text(text_item
)
4013 self
.title_hbox
.set_sensitive(False)
4014 self
.list_of_rules_combobox
.set_sensitive(False)
4015 self
.delete_rule_button
.set_sensitive(False)
4016 self
.open_rule_button
.set_sensitive(False)
4017 self
.privacy_list_active_checkbutton
.set_sensitive(False)
4018 self
.privacy_list_default_checkbutton
.set_sensitive(False)
4020 self
.list_of_rules_combobox
.set_active(0)
4021 self
.title_hbox
.set_sensitive(True)
4022 self
.list_of_rules_combobox
.set_sensitive(True)
4023 self
.delete_rule_button
.set_sensitive(True)
4024 self
.open_rule_button
.set_sensitive(True)
4025 self
.privacy_list_active_checkbutton
.set_sensitive(True)
4026 self
.privacy_list_default_checkbutton
.set_sensitive(True)
4028 gajim
.connections
[self
.account
].get_active_default_lists()
4030 def _nec_privacy_list_received(self
, obj
):
4031 if obj
.conn
.name
!= self
.account
:
4033 if obj
.list_name
!= self
.privacy_list_name
:
4035 self
.privacy_list_received(obj
.rules
)
4037 def refresh_rules(self
):
4038 gajim
.connections
[self
.account
].get_privacy_list(self
.privacy_list_name
)
4040 def on_delete_rule_button_clicked(self
, widget
):
4042 for rule
in self
.global_rules
:
4043 if rule
!= self
.list_of_rules_combobox
.get_active_text():
4044 tags
.append(self
.global_rules
[rule
])
4045 gajim
.connections
[self
.account
].set_privacy_list(
4046 self
.privacy_list_name
, tags
)
4047 self
.privacy_list_received(tags
)
4048 self
.add_edit_vbox
.hide()
4049 if not tags
: # we removed latest rule
4050 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4051 win
= gajim
.interface
.instances
[self
.account
]['privacy_lists']
4052 win
.remove_privacy_list_from_combobox(self
.privacy_list_name
)
4055 def on_open_rule_button_clicked(self
, widget
):
4056 self
.add_edit_rule_label
.set_label(
4057 _('<b>Edit a rule</b>'))
4058 active_num
= self
.list_of_rules_combobox
.get_active()
4059 if active_num
== -1:
4060 self
.active_rule
= ''
4062 self
.active_rule
= \
4063 self
.list_of_rules_combobox
.get_active_text().decode('utf-8')
4064 if self
.active_rule
!= '':
4065 rule_info
= self
.global_rules
[self
.active_rule
]
4066 self
.edit_order_spinbutton
.set_value(int(rule_info
['order']))
4067 if 'type' in rule_info
:
4068 if rule_info
['type'] == 'jid':
4069 self
.edit_type_jabberid_radiobutton
.set_active(True)
4070 self
.edit_type_jabberid_entry
.set_text(rule_info
['value'])
4071 elif rule_info
['type'] == 'group':
4072 self
.edit_type_group_radiobutton
.set_active(True)
4073 if rule_info
['value'] in self
.list_of_groups
:
4074 self
.edit_type_group_combobox
.set_active(
4075 self
.list_of_groups
[rule_info
['value']])
4077 self
.edit_type_group_combobox
.set_active(0)
4078 elif rule_info
['type'] == 'subscription':
4079 self
.edit_type_subscription_radiobutton
.set_active(True)
4080 sub_value
= rule_info
['value']
4081 if sub_value
== 'none':
4082 self
.edit_type_subscription_combobox
.set_active(0)
4083 elif sub_value
== 'both':
4084 self
.edit_type_subscription_combobox
.set_active(1)
4085 elif sub_value
== 'from':
4086 self
.edit_type_subscription_combobox
.set_active(2)
4087 elif sub_value
== 'to':
4088 self
.edit_type_subscription_combobox
.set_active(3)
4090 self
.edit_type_select_all_radiobutton
.set_active(True)
4092 self
.edit_type_select_all_radiobutton
.set_active(True)
4093 self
.edit_send_messages_checkbutton
.set_active(False)
4094 self
.edit_queries_send_checkbutton
.set_active(False)
4095 self
.edit_view_status_checkbutton
.set_active(False)
4096 self
.edit_send_status_checkbutton
.set_active(False)
4097 self
.edit_all_checkbutton
.set_active(False)
4098 if not rule_info
['child']:
4099 self
.edit_all_checkbutton
.set_active(True)
4101 if 'presence-out' in rule_info
['child']:
4102 self
.edit_send_status_checkbutton
.set_active(True)
4103 if 'presence-in' in rule_info
['child']:
4104 self
.edit_view_status_checkbutton
.set_active(True)
4105 if 'iq' in rule_info
['child']:
4106 self
.edit_queries_send_checkbutton
.set_active(True)
4107 if 'message' in rule_info
['child']:
4108 self
.edit_send_messages_checkbutton
.set_active(True)
4110 if rule_info
['action'] == 'allow':
4111 self
.edit_allow_radiobutton
.set_active(True)
4113 self
.edit_deny_radiobutton
.set_active(True)
4114 self
.add_edit_vbox
.show()
4116 def on_edit_all_checkbutton_toggled(self
, widget
):
4117 if widget
.get_active():
4118 self
.edit_send_messages_checkbutton
.set_active(True)
4119 self
.edit_queries_send_checkbutton
.set_active(True)
4120 self
.edit_view_status_checkbutton
.set_active(True)
4121 self
.edit_send_status_checkbutton
.set_active(True)
4122 self
.edit_send_messages_checkbutton
.set_sensitive(False)
4123 self
.edit_queries_send_checkbutton
.set_sensitive(False)
4124 self
.edit_view_status_checkbutton
.set_sensitive(False)
4125 self
.edit_send_status_checkbutton
.set_sensitive(False)
4127 self
.edit_send_messages_checkbutton
.set_active(False)
4128 self
.edit_queries_send_checkbutton
.set_active(False)
4129 self
.edit_view_status_checkbutton
.set_active(False)
4130 self
.edit_send_status_checkbutton
.set_active(False)
4131 self
.edit_send_messages_checkbutton
.set_sensitive(True)
4132 self
.edit_queries_send_checkbutton
.set_sensitive(True)
4133 self
.edit_view_status_checkbutton
.set_sensitive(True)
4134 self
.edit_send_status_checkbutton
.set_sensitive(True)
4136 def on_privacy_list_active_checkbutton_toggled(self
, widget
):
4137 if widget
.get_active():
4138 gajim
.connections
[self
.account
].set_active_list(
4139 self
.privacy_list_name
)
4141 gajim
.connections
[self
.account
].set_active_list(None)
4143 def on_privacy_list_default_checkbutton_toggled(self
, widget
):
4144 if widget
.get_active():
4145 gajim
.connections
[self
.account
].set_default_list(
4146 self
.privacy_list_name
)
4148 gajim
.connections
[self
.account
].set_default_list(None)
4150 def on_new_rule_button_clicked(self
, widget
):
4152 self
.add_edit_vbox
.show()
4154 def reset_fields(self
):
4155 self
.edit_type_jabberid_entry
.set_text('')
4156 self
.edit_allow_radiobutton
.set_active(True)
4157 self
.edit_type_jabberid_radiobutton
.set_active(True)
4158 self
.active_rule
= ''
4159 self
.edit_send_messages_checkbutton
.set_active(False)
4160 self
.edit_queries_send_checkbutton
.set_active(False)
4161 self
.edit_view_status_checkbutton
.set_active(False)
4162 self
.edit_send_status_checkbutton
.set_active(False)
4163 self
.edit_all_checkbutton
.set_active(False)
4164 self
.edit_order_spinbutton
.set_value(self
.max_order
+ 1)
4165 self
.edit_type_group_combobox
.set_active(0)
4166 self
.edit_type_subscription_combobox
.set_active(0)
4167 self
.add_edit_rule_label
.set_label(
4168 _('<b>Add a rule</b>'))
4170 def get_current_tags(self
):
4171 if self
.edit_type_jabberid_radiobutton
.get_active():
4173 edit_value
= self
.edit_type_jabberid_entry
.get_text()
4174 elif self
.edit_type_group_radiobutton
.get_active():
4176 edit_value
= self
.edit_type_group_combobox
.get_active_text()
4177 elif self
.edit_type_subscription_radiobutton
.get_active():
4178 edit_type
= 'subscription'
4179 subs
= ['none', 'both', 'from', 'to']
4180 edit_value
= subs
[self
.edit_type_subscription_combobox
.get_active()]
4181 elif self
.edit_type_select_all_radiobutton
.get_active():
4184 edit_order
= str(self
.edit_order_spinbutton
.get_value_as_int())
4185 if self
.edit_allow_radiobutton
.get_active():
4190 if not self
.edit_all_checkbutton
.get_active():
4191 if self
.edit_send_messages_checkbutton
.get_active():
4192 child
.append('message')
4193 if self
.edit_queries_send_checkbutton
.get_active():
4195 if self
.edit_send_status_checkbutton
.get_active():
4196 child
.append('presence-out')
4197 if self
.edit_view_status_checkbutton
.get_active():
4198 child
.append('presence-in')
4200 return {'order': edit_order
, 'action': edit_deny
,
4201 'type': edit_type
, 'value': edit_value
, 'child': child
}
4202 return {'order': edit_order
, 'action': edit_deny
, 'child': child
}
4204 def on_save_rule_button_clicked(self
, widget
):
4206 current_tags
= self
.get_current_tags()
4207 if int(current_tags
['order']) > self
.max_order
:
4208 self
.max_order
= int(current_tags
['order'])
4209 if self
.active_rule
== '':
4210 tags
.append(current_tags
)
4212 for rule
in self
.global_rules
:
4213 if rule
!= self
.active_rule
:
4214 tags
.append(self
.global_rules
[rule
])
4216 tags
.append(current_tags
)
4218 gajim
.connections
[self
.account
].set_privacy_list(
4219 self
.privacy_list_name
, tags
)
4220 self
.refresh_rules()
4221 self
.add_edit_vbox
.hide()
4222 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4223 win
= gajim
.interface
.instances
[self
.account
]['privacy_lists']
4224 win
.add_privacy_list_to_combobox(self
.privacy_list_name
)
4227 def on_list_of_rules_combobox_changed(self
, widget
):
4228 self
.add_edit_vbox
.hide()
4230 def on_edit_type_radiobutton_changed(self
, widget
, radiobutton
):
4231 active_bool
= widget
.get_active()
4233 self
.edit_rule_type
= radiobutton
4235 def on_edit_allow_radiobutton_changed(self
, widget
, radiobutton
):
4236 active_bool
= widget
.get_active()
4238 self
.allow_deny
= radiobutton
4240 def on_close_button_clicked(self
, widget
):
4241 self
.window
.destroy()
4243 class PrivacyListsWindow
:
4245 Window that is the main window for Privacy Lists; we can list there the
4246 privacy lists and ask to create a new one or edit an already there one
4248 def __init__(self
, account
):
4249 self
.account
= account
4250 self
.privacy_lists_save
= []
4252 self
.xml
= gtkgui_helpers
.get_gtk_builder('privacy_lists_window.ui')
4254 self
.window
= self
.xml
.get_object('privacy_lists_first_window')
4255 for widget_to_add
in ('list_of_privacy_lists_combobox',
4256 'delete_privacy_list_button', 'open_privacy_list_button',
4257 'new_privacy_list_button', 'new_privacy_list_entry',
4258 'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
4259 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
4261 self
.draw_privacy_lists_in_combobox([])
4262 self
.privacy_lists_refresh()
4266 if len(gajim
.connections
) > 1:
4267 title
= _('Privacy Lists for %s') % self
.account
4269 title
= _('Privacy Lists')
4271 self
.window
.set_title(title
)
4273 gajim
.ged
.register_event_handler('privacy-lists-received', ged
.GUI1
,
4274 self
._nec
_privacy
_lists
_received
)
4275 gajim
.ged
.register_event_handler('privacy-lists-removed', ged
.GUI1
,
4276 self
._nec
_privacy
_lists
_removed
)
4278 self
.window
.show_all()
4280 self
.xml
.connect_signals(self
)
4282 def on_privacy_lists_first_window_destroy(self
, widget
):
4283 if 'privacy_lists' in gajim
.interface
.instances
[self
.account
]:
4284 del gajim
.interface
.instances
[self
.account
]['privacy_lists']
4285 gajim
.ged
.remove_event_handler('privacy-lists-received', ged
.GUI1
,
4286 self
._nec
_privacy
_lists
_received
)
4287 gajim
.ged
.remove_event_handler('privacy-lists-removed', ged
.GUI1
,
4288 self
._nec
_privacy
_lists
_removed
)
4290 def remove_privacy_list_from_combobox(self
, privacy_list
):
4291 if privacy_list
not in self
.privacy_lists_save
:
4293 privacy_list_index
= self
.privacy_lists_save
.index(privacy_list
)
4294 self
.list_of_privacy_lists_combobox
.remove_text(privacy_list_index
)
4295 self
.privacy_lists_save
.remove(privacy_list
)
4297 def add_privacy_list_to_combobox(self
, privacy_list
):
4298 if privacy_list
in self
.privacy_lists_save
:
4300 self
.list_of_privacy_lists_combobox
.append_text(privacy_list
)
4301 self
.privacy_lists_save
.append(privacy_list
)
4303 def draw_privacy_lists_in_combobox(self
, privacy_lists
):
4304 self
.list_of_privacy_lists_combobox
.set_active(-1)
4305 self
.list_of_privacy_lists_combobox
.get_model().clear()
4306 self
.privacy_lists_save
= []
4307 for add_item
in privacy_lists
:
4308 self
.add_privacy_list_to_combobox(add_item
)
4311 def draw_widgets(self
):
4312 if len(self
.privacy_lists_save
) == 0:
4313 self
.list_of_privacy_lists_combobox
.set_sensitive(False)
4314 self
.open_privacy_list_button
.set_sensitive(False)
4315 self
.delete_privacy_list_button
.set_sensitive(False)
4317 self
.list_of_privacy_lists_combobox
.set_sensitive(True)
4318 self
.list_of_privacy_lists_combobox
.set_active(0)
4319 self
.open_privacy_list_button
.set_sensitive(True)
4320 self
.delete_privacy_list_button
.set_sensitive(True)
4322 def on_close_button_clicked(self
, widget
):
4323 self
.window
.destroy()
4325 def on_delete_privacy_list_button_clicked(self
, widget
):
4326 active_list
= self
.privacy_lists_save
[
4327 self
.list_of_privacy_lists_combobox
.get_active()]
4328 gajim
.connections
[self
.account
].del_privacy_list(active_list
)
4330 def privacy_list_removed(self
, active_list
):
4331 self
.privacy_lists_save
.remove(active_list
)
4332 self
.privacy_lists_received({'lists': self
.privacy_lists_save
})
4334 def _nec_privacy_lists_removed(self
, obj
):
4335 if obj
.conn
.name
!= self
.account
:
4337 self
.privacy_list_removed(obj
.lists_list
)
4339 def privacy_lists_received(self
, lists
):
4343 for privacy_list
in lists
['lists']:
4344 privacy_lists
.append(privacy_list
)
4345 self
.draw_privacy_lists_in_combobox(privacy_lists
)
4347 def _nec_privacy_lists_received(self
, obj
):
4348 if obj
.conn
.name
!= self
.account
:
4350 self
.privacy_lists_received(obj
.lists_list
)
4352 def privacy_lists_refresh(self
):
4353 gajim
.connections
[self
.account
].get_privacy_lists()
4355 def on_new_privacy_list_button_clicked(self
, widget
):
4356 name
= self
.new_privacy_list_entry
.get_text()
4358 ErrorDialog(_('Invalid List Name'),
4359 _('You must enter a name to create a privacy list.'))
4361 key_name
= 'privacy_list_%s' % name
4362 if key_name
in gajim
.interface
.instances
[self
.account
]:
4363 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
4365 gajim
.interface
.instances
[self
.account
][key_name
] = \
4366 PrivacyListWindow(self
.account
, name
, 'NEW')
4367 self
.new_privacy_list_entry
.set_text('')
4369 def on_privacy_lists_refresh_button_clicked(self
, widget
):
4370 self
.privacy_lists_refresh()
4372 def on_open_privacy_list_button_clicked(self
, widget
):
4373 name
= self
.privacy_lists_save
[
4374 self
.list_of_privacy_lists_combobox
.get_active()]
4375 key_name
= 'privacy_list_%s' % name
4376 if key_name
in gajim
.interface
.instances
[self
.account
]:
4377 gajim
.interface
.instances
[self
.account
][key_name
].window
.present()
4379 gajim
.interface
.instances
[self
.account
][key_name
] = \
4380 PrivacyListWindow(self
.account
, name
, 'EDIT')
4382 class InvitationReceivedDialog
:
4383 def __init__(self
, account
, room_jid
, contact_jid
, password
=None,
4384 comment
=None, is_continued
=False):
4386 self
.room_jid
= room_jid
4387 self
.account
= account
4388 self
.password
= password
4389 self
.is_continued
= is_continued
4391 pritext
= _('''You are invited to a groupchat''')
4392 #Don't translate $Contact
4394 sectext
= _('$Contact has invited you to join a discussion')
4396 sectext
= _('$Contact has invited you to group chat %(room_jid)s')\
4397 % {'room_jid': room_jid
}
4398 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, contact_jid
)
4399 contact_text
= contact
and contact
.name
or contact_jid
4400 sectext
= sectext
.replace('$Contact', contact_text
)
4402 if comment
: # only if not None and not ''
4403 comment
= gobject
.markup_escape_text(comment
)
4404 comment
= _('Comment: %s') % comment
4405 sectext
+= '\n\n%s' % comment
4406 sectext
+= '\n\n' + _('Do you want to accept the invitation?')
4408 def on_yes(checked
):
4410 if self
.is_continued
:
4411 gajim
.interface
.join_gc_room(self
.account
, self
.room_jid
,
4412 gajim
.nicks
[self
.account
], None, is_continued
=True)
4414 JoinGroupchatWindow(self
.account
, self
.room_jid
)
4415 except GajimGeneralException
:
4418 YesNoDialog(pritext
, sectext
, on_response_yes
=on_yes
)
4420 class ProgressDialog
:
4421 def __init__(self
, title_text
, during_text
, messages_queue
):
4423 During text is what to show during the procedure, messages_queue has the
4424 message to show in the textview
4426 self
.xml
= gtkgui_helpers
.get_gtk_builder('progress_dialog.ui')
4427 self
.dialog
= self
.xml
.get_object('progress_dialog')
4428 self
.label
= self
.xml
.get_object('label')
4429 self
.label
.set_markup('<big>' + during_text
+ '</big>')
4430 self
.progressbar
= self
.xml
.get_object('progressbar')
4431 self
.dialog
.set_title(title_text
)
4432 self
.dialog
.set_default_size(450, 250)
4433 self
.window
.set_position(gtk
.WIN_POS_CENTER_ON_PARENT
)
4434 self
.dialog
.show_all()
4435 self
.xml
.connect_signals(self
)
4437 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
4438 self
.update_progressbar
)
4440 def update_progressbar(self
):
4442 self
.progressbar
.pulse()
4443 return True # loop forever
4446 def on_progress_dialog_delete_event(self
, widget
, event
):
4447 return True # WM's X button or Escape key should not destroy the window
4450 class ClientCertChooserDialog(FileChooserDialog
):
4451 def __init__(self
, path_to_clientcert_file
='', on_response_ok
=None,
4452 on_response_cancel
=None):
4454 optionally accepts path_to_clientcert_file so it has that as selected
4456 def on_ok(widget
, callback
):
4458 check if file exists and call callback
4460 path_to_clientcert_file
= self
.get_filename()
4461 path_to_clientcert_file
= \
4462 gtkgui_helpers
.decode_filechooser_file_paths(
4463 (path_to_clientcert_file
,))[0]
4464 if os
.path
.exists(path_to_clientcert_file
):
4465 callback(widget
, path_to_clientcert_file
)
4467 FileChooserDialog
.__init
__(self
,
4468 title_text
=_('Choose Client Cert #PCKS12'),
4469 action
=gtk
.FILE_CHOOSER_ACTION_OPEN
,
4470 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4471 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4473 default_response
=gtk
.RESPONSE_OK
,
4474 on_response_ok
=(on_ok
, on_response_ok
),
4475 on_response_cancel
=on_response_cancel
)
4477 filter_
= gtk
.FileFilter()
4478 filter_
.set_name(_('All files'))
4479 filter_
.add_pattern('*')
4480 self
.add_filter(filter_
)
4482 filter_
= gtk
.FileFilter()
4483 filter_
.set_name(_('PKCS12 Files'))
4484 filter_
.add_pattern('*.p12')
4485 self
.add_filter(filter_
)
4486 self
.set_filter(filter_
)
4488 if path_to_clientcert_file
:
4489 # set_filename accept only absolute path
4490 path_to_clientcert_file
= os
.path
.abspath(path_to_clientcert_file
)
4491 self
.set_filename(path_to_clientcert_file
)
4494 class SoundChooserDialog(FileChooserDialog
):
4495 def __init__(self
, path_to_snd_file
='', on_response_ok
=None,
4496 on_response_cancel
=None):
4498 Optionally accepts path_to_snd_file so it has that as selected
4500 def on_ok(widget
, callback
):
4502 Check if file exists and call callback
4504 path_to_snd_file
= self
.get_filename()
4505 path_to_snd_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4506 (path_to_snd_file
,))[0]
4507 if os
.path
.exists(path_to_snd_file
):
4508 callback(widget
, path_to_snd_file
)
4510 FileChooserDialog
.__init
__(self
, title_text
= _('Choose Sound'),
4511 action
= gtk
.FILE_CHOOSER_ACTION_OPEN
,
4512 buttons
= (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4513 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4514 default_response
= gtk
.RESPONSE_OK
,
4515 current_folder
= gajim
.config
.get('last_sounds_dir'),
4516 on_response_ok
= (on_ok
, on_response_ok
),
4517 on_response_cancel
= on_response_cancel
)
4519 filter_
= gtk
.FileFilter()
4520 filter_
.set_name(_('All files'))
4521 filter_
.add_pattern('*')
4522 self
.add_filter(filter_
)
4524 filter_
= gtk
.FileFilter()
4525 filter_
.set_name(_('Wav Sounds'))
4526 filter_
.add_pattern('*.wav')
4527 self
.add_filter(filter_
)
4528 self
.set_filter(filter_
)
4530 path_to_snd_file
= helpers
.check_soundfile_path(path_to_snd_file
)
4531 if path_to_snd_file
:
4532 # set_filename accept only absolute path
4533 path_to_snd_file
= os
.path
.abspath(path_to_snd_file
)
4534 self
.set_filename(path_to_snd_file
)
4536 class ImageChooserDialog(FileChooserDialog
):
4537 def __init__(self
, path_to_file
='', on_response_ok
=None,
4538 on_response_cancel
=None):
4540 Optionally accepts path_to_snd_file so it has that as selected
4542 def on_ok(widget
, callback
):
4543 '''check if file exists and call callback'''
4544 path_to_file
= self
.get_filename()
4545 if not path_to_file
:
4547 path_to_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4549 if os
.path
.exists(path_to_file
):
4550 if isinstance(callback
, tuple):
4551 callback
[0](widget
, path_to_file
, *callback
[1:])
4553 callback(widget
, path_to_file
)
4557 path
= helpers
.get_my_pictures_path()
4559 path
= os
.environ
['HOME']
4562 FileChooserDialog
.__init
__(self
,
4563 title_text
= _('Choose Image'),
4564 action
= gtk
.FILE_CHOOSER_ACTION_OPEN
,
4565 buttons
= (gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4566 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4567 default_response
= gtk
.RESPONSE_OK
,
4568 current_folder
= path
,
4569 on_response_ok
= (on_ok
, on_response_ok
),
4570 on_response_cancel
= on_response_cancel
)
4572 if on_response_cancel
:
4573 self
.connect('destroy', on_response_cancel
)
4575 filter_
= gtk
.FileFilter()
4576 filter_
.set_name(_('All files'))
4577 filter_
.add_pattern('*')
4578 self
.add_filter(filter_
)
4580 filter_
= gtk
.FileFilter()
4581 filter_
.set_name(_('Images'))
4582 filter_
.add_mime_type('image/png')
4583 filter_
.add_mime_type('image/jpeg')
4584 filter_
.add_mime_type('image/gif')
4585 filter_
.add_mime_type('image/tiff')
4586 filter_
.add_mime_type('image/svg+xml')
4587 filter_
.add_mime_type('image/x-xpixmap') # xpm
4588 self
.add_filter(filter_
)
4589 self
.set_filter(filter_
)
4592 self
.set_filename(path_to_file
)
4594 self
.set_use_preview_label(False)
4595 self
.set_preview_widget(gtk
.Image())
4596 self
.connect('selection-changed', self
.update_preview
)
4598 def update_preview(self
, widget
):
4599 path_to_file
= widget
.get_preview_filename()
4600 if path_to_file
is None or os
.path
.isdir(path_to_file
):
4601 # nothing to preview or directory
4602 # make sure you clean image do show nothing
4603 widget
.get_preview_widget().set_from_file(None)
4606 pixbuf
= gtk
.gdk
.pixbuf_new_from_file_at_size(path_to_file
, 100, 100)
4607 except gobject
.GError
:
4609 widget
.get_preview_widget().set_from_pixbuf(pixbuf
)
4611 class AvatarChooserDialog(ImageChooserDialog
):
4612 def __init__(self
, path_to_file
='', on_response_ok
=None,
4613 on_response_cancel
=None, on_response_clear
=None):
4614 ImageChooserDialog
.__init
__(self
, path_to_file
, on_response_ok
,
4616 button
= gtk
.Button(None, gtk
.STOCK_CLEAR
)
4617 self
.response_clear
= on_response_clear
4618 if on_response_clear
:
4619 button
.connect('clicked', self
.on_clear
)
4621 self
.action_area
.pack_start(button
)
4622 self
.action_area
.reorder_child(button
, 0)
4624 def on_clear(self
, widget
):
4625 if isinstance(self
.response_clear
, tuple):
4626 self
.response_clear
[0](widget
, *self
.response_clear
[1:])
4628 self
.response_clear(widget
)
4631 class ArchiveChooserDialog(FileChooserDialog
):
4632 def __init__(self
, on_response_ok
=None, on_response_cancel
=None):
4634 def on_ok(widget
, callback
):
4635 '''check if file exists and call callback'''
4636 path_to_file
= self
.get_filename()
4637 if not path_to_file
:
4639 path_to_file
= gtkgui_helpers
.decode_filechooser_file_paths(
4641 if os
.path
.exists(path_to_file
):
4642 if isinstance(callback
, tuple):
4643 callback
[0](path_to_file
, *callback
[1:])
4645 callback(path_to_file
)
4648 path
= helpers
.get_documents_path()
4650 FileChooserDialog
.__init
__(self
,
4651 title_text
=_('Choose Archive'),
4652 action
=gtk
.FILE_CHOOSER_ACTION_OPEN
,
4653 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
4654 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
),
4655 default_response
=gtk
.RESPONSE_OK
,
4656 current_folder
=path
,
4657 on_response_ok
=(on_ok
, on_response_ok
),
4658 on_response_cancel
=on_response_cancel
)
4660 if on_response_cancel
:
4661 self
.connect('destroy', on_response_cancel
)
4663 filter_
= gtk
.FileFilter()
4664 filter_
.set_name(_('All files'))
4665 filter_
.add_pattern('*')
4666 self
.add_filter(filter_
)
4668 filter_
= gtk
.FileFilter()
4669 filter_
.set_name(_('Zip files'))
4670 filter_
.add_pattern('*.zip')
4672 self
.add_filter(filter_
)
4673 self
.set_filter(filter_
)
4676 class AddSpecialNotificationDialog
:
4677 def __init__(self
, jid
):
4679 jid is the jid for which we want to add special notification (sound and
4680 notification popups)
4682 self
.xml
= gtkgui_helpers
.get_gtk_builder(
4683 'add_special_notification_window.ui')
4684 self
.window
= self
.xml
.get_object('add_special_notification_window')
4685 self
.condition_combobox
= self
.xml
.get_object('condition_combobox')
4686 self
.condition_combobox
.set_active(0)
4687 self
.notification_popup_yes_no_combobox
= self
.xml
.get_object(
4688 'notification_popup_yes_no_combobox')
4689 self
.notification_popup_yes_no_combobox
.set_active(0)
4690 self
.listen_sound_combobox
= self
.xml
.get_object('listen_sound_combobox')
4691 self
.listen_sound_combobox
.set_active(0)
4694 self
.xml
.get_object('when_foo_becomes_label').set_text(
4695 _('When %s becomes:') % self
.jid
)
4697 self
.window
.set_title(_('Adding Special Notification for %s') % jid
)
4698 self
.window
.show_all()
4699 self
.xml
.connect_signals(self
)
4701 def on_cancel_button_clicked(self
, widget
):
4702 self
.window
.destroy()
4704 def on_add_special_notification_window_delete_event(self
, widget
, event
):
4705 self
.window
.destroy()
4707 def on_listen_sound_combobox_changed(self
, widget
):
4708 active
= widget
.get_active()
4709 if active
== 1: # user selected 'choose sound'
4710 def on_ok(widget
, path_to_snd_file
):
4712 #print path_to_snd_file
4714 def on_cancel(widget
):
4715 widget
.set_active(0) # go back to No Sound
4717 self
.dialog
= SoundChooserDialog(on_response_ok
=on_ok
,
4718 on_response_cancel
=on_cancel
)
4720 def on_ok_button_clicked(self
, widget
):
4721 conditions
= ('online', 'chat', 'online_and_chat',
4722 'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline')
4723 active
= self
.condition_combobox
.get_active()
4725 active_iter
= self
.listen_sound_combobox
.get_active_iter()
4726 listen_sound_model
= self
.listen_sound_combobox
.get_model()
4728 class TransformChatToMUC
:
4729 # Keep a reference on windows so garbage collector don't restroy them
4731 def __init__(self
, account
, jids
, preselected
=None):
4733 This window is used to trasform a one-to-one chat to a MUC. We do 2
4734 things: first select the server and then make a guests list
4737 self
.instances
.append(self
)
4738 self
.account
= account
4739 self
.auto_jids
= jids
4740 self
.preselected_jids
= preselected
4742 self
.xml
= gtkgui_helpers
.get_gtk_builder('chat_to_muc_window.ui')
4743 self
.window
= self
.xml
.get_object('chat_to_muc_window')
4745 for widget_to_add
in ('invite_button', 'cancel_button',
4746 'server_list_comboboxentry', 'guests_treeview',
4747 'server_and_guests_hseparator', 'server_select_label'):
4748 self
.__dict
__[widget_to_add
] = self
.xml
.get_object(widget_to_add
)
4751 self
.servers
= gtk
.ListStore(str)
4752 self
.server_list_comboboxentry
.set_model(self
.servers
)
4754 self
.server_list_comboboxentry
.set_text_column(0)
4756 # get the muc server of our server
4757 if 'jabber' in gajim
.connections
[account
].muc_jid
:
4758 server_list
.append(gajim
.connections
[account
].muc_jid
['jabber'])
4759 # add servers or recently joined groupchats
4760 recently_groupchat
= gajim
.config
.get('recently_groupchat').split()
4761 for g
in recently_groupchat
:
4762 server
= gajim
.get_server_from_jid(g
)
4763 if server
not in server_list
and not server
.startswith('irc'):
4764 server_list
.append(server
)
4765 # add a default server
4767 server_list
.append('conference.jabber.org')
4769 for s
in server_list
:
4770 self
.servers
.append([s
])
4772 self
.server_list_comboboxentry
.set_active(0)
4776 self
.store
= gtk
.ListStore(gtk
.gdk
.Pixbuf
, str, str)
4777 self
.store
.set_sort_column_id(1, gtk
.SORT_ASCENDING
)
4778 self
.guests_treeview
.set_model(self
.store
)
4780 renderer1
= gtk
.CellRendererText()
4781 renderer2
= gtk
.CellRendererPixbuf()
4782 column
= gtk
.TreeViewColumn('Status', renderer2
, pixbuf
=0)
4783 self
.guests_treeview
.append_column(column
)
4784 column
= gtk
.TreeViewColumn('Name', renderer1
, text
=1)
4785 self
.guests_treeview
.append_column(column
)
4787 self
.guests_treeview
.get_selection().set_mode(gtk
.SELECTION_MULTIPLE
)
4789 # All contacts beside the following can be invited:
4790 # transports, zeroconf contacts, minimized groupchats
4791 def invitable(contact
, contact_transport
=None):
4792 return (contact
.jid
not in self
.auto_jids
and
4793 contact
.jid
!= gajim
.get_jid_from_account(self
.account
) and
4794 contact
.jid
not in gajim
.interface
.minimized_controls
[account
] and
4795 not contact
.is_transport() and
4796 not contact_transport
)
4798 # set jabber id and pseudos
4799 for account
in gajim
.contacts
.get_accounts():
4800 if gajim
.connections
[account
].is_zeroconf
:
4802 for jid
in gajim
.contacts
.get_jid_list(account
):
4803 contact
= gajim
.contacts
.get_contact_with_highest_priority(
4805 contact_transport
= gajim
.get_transport_name_from_jid(jid
)
4806 # Add contact if it can be invited
4807 if invitable(contact
, contact_transport
) and \
4808 contact
.show
not in ('offline', 'error'):
4809 img
= gajim
.interface
.jabber_state_images
['16'][contact
.show
]
4812 name
= jid
.split('@')[0]
4813 iter_
= self
.store
.append([img
.get_pixbuf(), name
, jid
])
4814 # preselect treeview rows
4815 if self
.preselected_jids
and jid
in self
.preselected_jids
:
4816 path
= self
.store
.get_path(iter_
)
4817 self
.guests_treeview
.get_selection().select_path(path
)
4819 gajim
.ged
.register_event_handler('unique-room-id-supported', ged
.GUI1
,
4820 self
._nec
_unique
_room
_id
_supported
)
4821 gajim
.ged
.register_event_handler('unique-room-id-not-supported',
4822 ged
.GUI1
, self
._nec
_unique
_room
_id
_not
_supported
)
4825 self
.window
.show_all()
4827 self
.xml
.connect_signals(self
)
4829 def on_chat_to_muc_window_destroy(self
, widget
):
4830 gajim
.ged
.remove_event_handler('unique-room-id-supported', ged
.GUI1
,
4831 self
._nec
_unique
_room
_id
_supported
)
4832 gajim
.ged
.remove_event_handler('unique-room-id-not-supported', ged
.GUI1
,
4833 self
._nec
_unique
_room
_id
_not
_supported
)
4834 self
.instances
.remove(self
)
4836 def on_chat_to_muc_window_key_press_event(self
, widget
, event
):
4837 if event
.keyval
== gtk
.keysyms
.Escape
: # ESCAPE
4838 self
.window
.destroy()
4840 def on_invite_button_clicked(self
, widget
):
4841 server
= self
.server_list_comboboxentry
.get_active_text()
4844 gajim
.connections
[self
.account
].check_unique_room_id_support(server
, self
)
4846 def _nec_unique_room_id_supported(self
, obj
):
4847 if obj
.instance
!= self
:
4850 guests
= self
.guests_treeview
.get_selection().get_selected_rows()
4851 for guest
in guests
[1]:
4852 iter_
= self
.store
.get_iter(guest
)
4853 guest_list
.append(self
.store
[iter_
][2].decode('utf-8'))
4854 for guest
in self
.auto_jids
:
4855 guest_list
.append(guest
)
4856 room_jid
= obj
.room_id
+ '@' + obj
.server
4857 gajim
.automatic_rooms
[self
.account
][room_jid
] = {}
4858 gajim
.automatic_rooms
[self
.account
][room_jid
]['invities'] = guest_list
4859 gajim
.automatic_rooms
[self
.account
][room_jid
]['continue_tag'] = True
4860 gajim
.interface
.join_gc_room(self
.account
, room_jid
,
4861 gajim
.nicks
[self
.account
], None, is_continued
=True)
4862 self
.window
.destroy()
4864 def on_cancel_button_clicked(self
, widget
):
4865 self
.window
.destroy()
4867 def _nec_unique_room_id_not_supported(self
, obj
):
4868 if obj
.instance
!= self
:
4870 obj
.room_id
= gajim
.nicks
[self
.account
].lower().replace(' ', '') + \
4871 str(randrange(9999999))
4872 self
._nec
_unique
_room
_id
_supported
(obj
)
4874 class DataFormWindow(Dialog
):
4875 def __init__(self
, form
, on_response_ok
):
4876 self
.df_response_ok
= on_response_ok
4877 Dialog
.__init
__(self
, None, 'test', [(gtk
.STOCK_CANCEL
,
4878 gtk
.RESPONSE_REJECT
), (gtk
.STOCK_OK
, gtk
.RESPONSE_ACCEPT
)],
4879 on_response_ok
=self
.on_ok
)
4880 self
.set_resizable(True)
4881 gtkgui_helpers
.resize_window(self
, 600, 400)
4882 self
.dataform_widget
= dataforms_widget
.DataFormWidget()
4883 self
.dataform
= dataforms
.ExtendForm(node
=form
)
4884 self
.dataform_widget
.set_sensitive(True)
4885 self
.dataform_widget
.data_form
= self
.dataform
4886 self
.dataform_widget
.show_all()
4887 self
.vbox
.pack_start(self
.dataform_widget
)
4890 form
= self
.dataform_widget
.data_form
4891 if isinstance(self
.df_response_ok
, tuple):
4892 self
.df_response_ok
[0](form
, *self
.df_response_ok
[1:])
4894 self
.df_response_ok(form
)
4897 class ESessionInfoWindow
:
4899 Class for displaying information about a XEP-0116 encrypted session
4901 def __init__(self
, session
):
4902 self
.session
= session
4904 self
.xml
= gtkgui_helpers
.get_gtk_builder('esession_info_window.ui')
4905 self
.xml
.connect_signals(self
)
4907 self
.security_image
= self
.xml
.get_object('security_image')
4908 self
.verify_now_button
= self
.xml
.get_object('verify_now_button')
4909 self
.button_label
= self
.xml
.get_object('button_label')
4910 self
.window
= self
.xml
.get_object('esession_info_window')
4914 self
.window
.show_all()
4916 def update_info(self
):
4917 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
}
4919 if self
.session
.verified_identity
:
4920 labeltext
+= '\n\n' + _('''You have already verified this contact's identity.''')
4921 security_image
= 'gajim-security_high'
4922 if self
.session
.control
:
4923 self
.session
.control
._show
_lock
_image
(True, 'E2E', True,
4924 self
.session
.is_loggable(), True)
4926 verification_status
= _('''Contact's identity verified''')
4927 self
.window
.set_title(verification_status
)
4928 self
.xml
.get_object('verification_status_label').set_markup(
4929 '<b><span size="x-large">%s</span></b>' % verification_status
)
4931 self
.xml
.get_object('dialog-action_area1').set_no_show_all(True)
4932 self
.button_label
.set_text(_('Verify again...'))
4934 if self
.session
.control
:
4935 self
.session
.control
._show
_lock
_image
(True, 'E2E', True,
4936 self
.session
.is_loggable(), False)
4937 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.''')
4938 security_image
= 'gajim-security_low'
4940 verification_status
= _('''Contact's identity NOT verified''')
4941 self
.window
.set_title(verification_status
)
4942 self
.xml
.get_object('verification_status_label').set_markup(
4943 '<b><span size="x-large">%s</span></b>' % verification_status
)
4945 self
.button_label
.set_text(_('Verify...'))
4947 path
= gtkgui_helpers
.get_icon_path(security_image
, 32)
4948 self
.security_image
.set_from_file(path
)
4950 self
.xml
.get_object('info_display').set_markup(labeltext
)
4952 def on_close_button_clicked(self
, widget
):
4953 self
.window
.destroy()
4955 def on_verify_now_button_clicked(self
, widget
):
4956 pritext
= _('''Have you verified the contact's identity?''')
4957 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
}
4958 sectext
+= '\n\n' + _('Did you talk to the remote contact and verify the SAS?')
4960 def on_yes(checked
):
4961 self
.session
._verified
_srs
_cb
()
4962 self
.session
.verified_identity
= True
4966 self
.session
._unverified
_srs
_cb
()
4967 self
.session
.verified_identity
= False
4970 YesNoDialog(pritext
, sectext
, on_response_yes
=on_yes
, on_response_no
=on_no
)
4972 class GPGInfoWindow
:
4974 Class for displaying information about a XEP-0116 encrypted session
4976 def __init__(self
, control
):
4977 xml
= gtkgui_helpers
.get_gtk_builder('esession_info_window.ui')
4978 security_image
= xml
.get_object('security_image')
4979 status_label
= xml
.get_object('verification_status_label')
4980 info_label
= xml
.get_object('info_display')
4981 verify_now_button
= xml
.get_object('verify_now_button')
4982 self
.window
= xml
.get_object('esession_info_window')
4983 account
= control
.account
4984 keyID
= control
.contact
.keyID
4987 verify_now_button
.set_no_show_all(True)
4988 verify_now_button
.hide()
4990 if keyID
.endswith('MISMATCH'):
4991 verification_status
= _('''Contact's identity NOT verified''')
4992 info
= _('The contact\'s key (%s) <b>does not match</b> the key '
4993 'assigned in Gajim.') % keyID
[:8]
4994 image
= 'gajim-security_low'
4996 # No key assigned nor a key is used by remote contact
4997 verification_status
= _('No GPG key assigned')
4998 info
= _('No GPG key is assigned to this contact. So you cannot '
4999 'encrypt messages.')
5000 image
= 'gajim-security_low'
5002 error
= gajim
.connections
[account
].gpg
.encrypt('test', [keyID
])[1]
5004 verification_status
= _('''Contact's identity NOT verified''')
5005 info
= _('GPG key is assigned to this contact, but <b>you do not '
5006 'trust his key</b>, so message <b>cannot</b> be encrypted. Use '
5007 'your GPG client to trust this key.')
5008 image
= 'gajim-security_low'
5010 verification_status
= _('''Contact's identity verified''')
5011 info
= _('GPG Key is assigned to this contact, and you trust his '
5012 'key, so messages will be encrypted.')
5013 image
= 'gajim-security_high'
5015 status_label
.set_markup('<b><span size="x-large">%s</span></b>' % \
5016 verification_status
)
5017 info_label
.set_markup(info
)
5019 path
= gtkgui_helpers
.get_icon_path(image
, 32)
5020 security_image
.set_from_file(path
)
5022 xml
.connect_signals(self
)
5023 self
.window
.show_all()
5025 def on_close_button_clicked(self
, widget
):
5026 self
.window
.destroy()
5030 class ResourceConflictDialog(TimeoutDialog
, InputDialog
):
5031 def __init__(self
, title
, text
, resource
, ok_handler
):
5032 TimeoutDialog
.__init
__(self
, 15, self
.on_timeout
)
5033 InputDialog
.__init
__(self
, title
, text
, input_str
=resource
,
5034 is_modal
=False, ok_handler
=ok_handler
)
5035 self
.title_text
= title
5038 def on_timeout(self
):
5039 self
.on_okbutton_clicked(None)
5043 class VoIPCallReceivedDialog(object):
5045 def __init__(self
, account
, contact_jid
, sid
, content_types
):
5046 self
.instances
[(contact_jid
, sid
)] = self
5047 self
.account
= account
5048 self
.fjid
= contact_jid
5050 self
.content_types
= content_types
5052 xml
= gtkgui_helpers
.get_gtk_builder('voip_call_received_dialog.ui')
5053 xml
.connect_signals(self
)
5055 jid
= gajim
.get_jid_without_resource(self
.fjid
)
5056 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
5057 if contact
and contact
.name
:
5058 self
.contact_text
= '%s (%s)' % (contact
.name
, jid
)
5060 self
.contact_text
= contact_jid
5062 self
.dialog
= xml
.get_object('voip_call_received_messagedialog')
5063 self
.set_secondary_text()
5065 self
.dialog
.show_all()
5068 def get_dialog(cls
, jid
, sid
):
5069 if (jid
, sid
) in cls
.instances
:
5070 return cls
.instances
[(jid
, sid
)]
5074 def set_secondary_text(self
):
5075 if 'audio' in self
.content_types
and 'video' in self
.content_types
:
5076 types_text
= _('an audio and video')
5077 elif 'audio' in self
.content_types
:
5078 types_text
= _('an audio')
5079 elif 'video' in self
.content_types
:
5080 types_text
= _('a video')
5082 # do the substitution
5083 self
.dialog
.set_property('secondary-text',
5084 _('%(contact)s wants to start %(type)s session with you. Do you want '
5085 'to answer the call?') % {'contact': self
.contact_text
,
5086 'type': types_text
})
5088 def add_contents(self
, content_types
):
5089 for type_
in content_types
:
5090 if type_
not in self
.content_types
:
5091 self
.content_types
.add(type_
)
5092 self
.set_secondary_text()
5094 def remove_contents(self
, content_types
):
5095 for type_
in content_types
:
5096 if type_
in self
.content_types
:
5097 self
.content_types
.remove(type_
)
5098 if not self
.content_types
:
5099 self
.dialog
.destroy()
5101 self
.set_secondary_text()
5103 def on_voip_call_received_messagedialog_destroy(self
, dialog
):
5104 if (self
.fjid
, self
.sid
) in self
.instances
:
5105 del self
.instances
[(self
.fjid
, self
.sid
)]
5107 def on_voip_call_received_messagedialog_close(self
, dialog
):
5108 return self
.on_voip_call_received_messagedialog_response(dialog
,
5111 def on_voip_call_received_messagedialog_response(self
, dialog
, response
):
5112 # we've got response from user, either stop connecting or accept the call
5113 session
= gajim
.connections
[self
.account
].get_jingle_session(self
.fjid
,
5117 if response
== gtk
.RESPONSE_YES
:
5118 #TODO: Ensure that ctrl.contact.resource == resource
5119 jid
= gajim
.get_jid_without_resource(self
.fjid
)
5120 resource
= gajim
.get_resource_from_jid(self
.fjid
)
5121 ctrl
= (gajim
.interface
.msg_win_mgr
.get_control(self
.fjid
, self
.account
)
5122 or gajim
.interface
.msg_win_mgr
.get_control(jid
, self
.account
)
5123 or gajim
.interface
.new_chat_from_jid(self
.account
, jid
))
5125 # Chat control opened, update content's status
5126 audio
= session
.get_content('audio')
5127 video
= session
.get_content('video')
5128 if audio
and not audio
.negotiated
:
5129 ctrl
.set_audio_state('connecting', self
.sid
)
5130 if video
and not video
.negotiated
:
5131 ctrl
.set_video_state('connecting', self
.sid
)
5132 # Now, accept the content/sessions.
5133 # This should be done after the chat control is running
5134 if not session
.accepted
:
5135 session
.approve_session()
5136 for content
in self
.content_types
:
5137 session
.approve_content(content
)
5138 else: # response==gtk.RESPONSE_NO
5139 if not session
.accepted
:
5140 session
.decline_session()
5142 for content
in self
.content_types
:
5143 session
.reject_content(content
)
5147 class CertificatDialog(InformationDialog
):
5148 def __init__(self
, parent
, account
, cert
):
5149 issuer
= cert
.get_issuer()
5150 subject
= cert
.get_subject()
5151 InformationDialog
.__init
__(self
,
5152 _('Certificate for account %s') % account
, _('''<b>Issued to:</b>
5153 Common Name (CN): %(scn)s
5154 Organization (O): %(sorg)s
5155 Organizationl Unit (OU): %(sou)s
5156 Serial Number: %(sn)s
5159 Common Name (CN): %(icn)s
5160 Organization (O): %(iorg)s
5161 Organizationl Unit (OU): %(iou)s
5168 SHA1 Fingerprint: %(sha1)s''') % {
5169 'scn': subject
.commonName
, 'sorg': subject
.organizationName
,
5170 'sou': subject
.organizationalUnitName
,
5171 'sn': cert
.get_serial_number(), 'icn': issuer
.commonName
,
5172 'iorg': issuer
.organizationName
,
5173 'iou': issuer
.organizationalUnitName
,
5174 'io': cert
.get_notBefore(), 'eo': cert
.get_notAfter(),
5175 'sha1': cert
.digest('sha1')})
5176 self
.set_transient_for(parent
)
5179 class CheckFingerprintDialog(YesNoDialog
):
5180 def __init__(self
, pritext
='', sectext
='', checktext
='',
5181 on_response_yes
=None, on_response_no
=None, account
=None, certificate
=None):
5182 self
.account
= account
5183 self
.cert
= certificate
5184 YesNoDialog
.__init
__(self
, pritext
, sectext
, checktext
, on_response_yes
,
5186 b
= gtk
.Button(_('View cert...'))
5187 b
.connect('clicked', self
.on_cert_clicked
)
5189 area
= self
.get_action_area()
5192 def on_cert_clicked(self
, button
):
5193 d
= CertificatDialog(self
, self
.account
, self
.cert
)
5195 class SSLErrorDialog(ConfirmationDialogDoubleCheck
):
5196 def __init__(self
, account
, certificate
, pritext
, sectext
, checktext1
,
5197 checktext2
, on_response_ok
=None, on_response_cancel
=None):
5198 self
.account
= account
5199 self
.cert
= certificate
5200 ConfirmationDialogDoubleCheck
.__init
__(self
, pritext
, sectext
,
5201 checktext1
, checktext2
, on_response_ok
, on_response_cancel
,
5203 b
= gtk
.Button(_('View cert...'))
5204 b
.connect('clicked', self
.on_cert_clicked
)
5206 area
= self
.get_action_area()
5209 def on_cert_clicked(self
, button
):
5210 d
= CertificatDialog(self
, self
.account
, self
.cert
)