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