dialogs.py 51.6 KB
Newer Older
1
##	dialogs.py
Yann Leboulanger's avatar
Yann Leboulanger committed
2
##
3
4
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
5
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
6
7
8
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
Yann Leboulanger's avatar
Yann Leboulanger committed
9
10
11
12
13
14
15
16
17
18
19
20
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##

import gtk
21
22
import gtk.glade
import gobject
Yann Leboulanger's avatar
Yann Leboulanger committed
23
import os
24
import Queue
25

nkour's avatar
nkour committed
26
import gtkgui_helpers
nkour's avatar
nkour committed
27
import vcard
28
import conversation_textview
29

30
31
32
33
34
35
try:
	import gtkspell
	HAS_GTK_SPELL = True
except:
	HAS_GTK_SPELL = False

36
from filetransfers_window import FileTransfersWindow
37
from gajim_themes_window import GajimThemesWindow
38
from advanced import AdvancedConfigurationWindow
39
from common import gajim
40
from common import helpers
41
from common import i18n
42

43
44
45
46
_ = i18n._
APP = i18n.APP
gtk.glade.bindtextdomain (APP, i18n.DIR)
gtk.glade.textdomain (APP)
Yann Leboulanger's avatar
Yann Leboulanger committed
47

48
GTKGUI_GLADE = 'gtkgui.glade'
Yann Leboulanger's avatar
Yann Leboulanger committed
49

50
class EditGroupsDialog:
nkour's avatar
nkour committed
51
	'''Class for the edit group dialog window'''
52
	def __init__(self, user, account):
53
54
55
56
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'edit_groups_dialog', APP)
		self.dialog = self.xml.get_widget('edit_groups_dialog')
		self.account = account
		self.user = user
57
		self.changes_made = False
58
		self.list = self.xml.get_widget('groups_treeview')
59
		self.xml.get_widget('nickname_label').set_markup(
60
			_("Contact's name: <i>%s</i>") % user.get_shown_name())
61
		self.xml.get_widget('jid_label').set_markup(
62
			_('JID: <i>%s</i>') % user.jid)
63
		
64
65
66
67
		self.xml.signal_autoconnect(self)
		self.init_list()

	def run(self):
68
		self.dialog.show_all()
69
		if self.changes_made:
nkour's avatar
nkour committed
70
			gajim.connections[self.account].update_contact(self.user.jid,
71
				self.user.name, self.user.groups)
72

73
74
75
76
	def on_edit_groups_dialog_response(self, widget, response_id):
		if response_id == gtk.RESPONSE_CLOSE:
			self.dialog.destroy()

nkour's avatar
nkour committed
77
	def update_contact(self):
78
79
		gajim.interface.roster.remove_contact(self.user, self.account)
		gajim.interface.roster.add_contact_to_roster(self.user.jid, self.account)
80
81
		gajim.connections[self.account].update_contact(self.user.jid,
			self.user.name, self.user.groups)
82
83

	def on_add_button_clicked(self, widget):
84
		group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
85
86
87
88
89
90
		if not group:
			return
		# check if it already exists
		model = self.list.get_model()
		iter = model.get_iter_root()
		while iter:
91
			if model.get_value(iter, 0).decode('utf-8') == group:
92
93
				return
			iter = model.iter_next(iter)
94
		self.changes_made = True
95
96
		model.append((group, True))
		self.user.groups.append(group)
nkour's avatar
nkour committed
97
		self.update_contact()
98
99

	def group_toggled_cb(self, cell, path):
100
		self.changes_made = True
101
102
103
		model = self.list.get_model()
		model[path][1] = not model[path][1]
		if model[path][1]:
104
			self.user.groups.append(model[path][0].decode('utf-8'))
105
		else:
106
			self.user.groups.remove(model[path][0].decode('utf-8'))
nkour's avatar
nkour committed
107
		self.update_contact()
108
109

	def init_list(self):
110
		store = gtk.ListStore(str, bool)
111
		self.list.set_model(store)
112
		for g in gajim.groups[self.account].keys():
113
			if g in (_('Transports'), _('Not in Roster')):
114
				continue
115
116
117
118
119
120
121
			iter = store.append()
			store.set(iter, 0, g)
			if g in self.user.groups:
				store.set(iter, 1, True)
			else:
				store.set(iter, 1, False)
		column = gtk.TreeViewColumn(_('Group'))
nkour's avatar
nkour committed
122
		column.set_expand(True)
123
124
125
		self.list.append_column(column)
		renderer = gtk.CellRendererText()
		column.pack_start(renderer)
126
		column.set_attributes(renderer, text = 0)
127
128
		
		column = gtk.TreeViewColumn(_('In the group'))
nkour's avatar
nkour committed
129
		column.set_expand(False)
130
131
132
133
134
		self.list.append_column(column)
		renderer = gtk.CellRendererToggle()
		column.pack_start(renderer)
		renderer.set_property('activatable', True)
		renderer.connect('toggled', self.group_toggled_cb)
135
		column.set_attributes(renderer, active = 1)
136

137
class PassphraseDialog:
nkour's avatar
nkour committed
138
	'''Class for Passphrase dialog'''
Yann Leboulanger's avatar
Yann Leboulanger committed
139
	def run(self):
nkour's avatar
nkour committed
140
		'''Wait for OK button to be pressed and return passphrase/password'''
141
		rep = self.window.run()
Yann Leboulanger's avatar
Yann Leboulanger committed
142
		if rep == gtk.RESPONSE_OK:
143
			passphrase = self.passphrase_entry.get_text().decode('utf-8')
Yann Leboulanger's avatar
Yann Leboulanger committed
144
		else:
145
146
147
148
			passphrase = -1
		save_passphrase_checkbutton = self.xml.\
			get_widget('save_passphrase_checkbutton')
		self.window.destroy()
149
		return passphrase, save_passphrase_checkbutton.get_active()
150

151
	def __init__(self, titletext, labeltext, checkbuttontext):
152
153
154
155
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'passphrase_dialog', APP)
		self.window = self.xml.get_widget('passphrase_dialog')
		self.passphrase_entry = self.xml.get_widget('passphrase_entry')
		self.passphrase = -1
156
		self.window.set_title(titletext)
157
		self.xml.get_widget('message_label').set_text(labeltext)
158
159
		self.xml.get_widget('save_passphrase_checkbutton').set_label(
			checkbuttontext)
160
		self.xml.signal_autoconnect(self)
161
		self.window.show_all()
Yann Leboulanger's avatar
Yann Leboulanger committed
162

163
class ChooseGPGKeyDialog:
nkour's avatar
nkour committed
164
	'''Class for GPG key dialog'''
165
	def __init__(self, title_text, prompt_text, secret_keys, selected = None):
166
		#list : {keyID: userName, ...}
167
168
		xml = gtk.glade.XML(GTKGUI_GLADE, 'choose_gpg_key_dialog', APP)
		self.window = xml.get_widget('choose_gpg_key_dialog')
169
		self.window.set_title(title_text)
170
		self.keys_treeview = xml.get_widget('keys_treeview')
171
172
		prompt_label = xml.get_widget('prompt_label')
		prompt_label.set_text(prompt_text)
173
		model = gtk.ListStore(str, str)
174
		model.set_sort_column_id(1, gtk.SORT_ASCENDING)
175
		self.keys_treeview.set_model(model)
176
177
		#columns
		renderer = gtk.CellRendererText()
178
		self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
179
			renderer, text = 0)
180
		renderer = gtk.CellRendererText()
nkour's avatar
nkour committed
181
		self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
182
			renderer, text = 1)
183
		self.fill_tree(secret_keys, selected)
184
185
		self.window.show_all()

186
187
188
	def run(self):
		rep = self.window.run()
		if rep == gtk.RESPONSE_OK:
189
190
			selection = self.keys_treeview.get_selection()
			(model, iter) = selection.get_selected()
nkour's avatar
nkour committed
191
192
			keyID = [ model[iter][0].decode('utf-8'),
				model[iter][1].decode('utf-8') ]
193
194
		else:
			keyID = None
195
		self.window.destroy()
196
197
		return keyID

198
199
200
201
202
203
204
205
206
	def fill_tree(self, list, selected):
		model = self.keys_treeview.get_model()
		for keyID in list.keys():
			iter = model.append((keyID, list[keyID]))
			if keyID == selected:
				path = model.get_path(iter)
				self.keys_treeview.set_cursor(path)


207
class ChangeStatusMessageDialog:
208
	def __init__(self, show = None):
209
		self.show = show
210
211
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'change_status_message_dialog',
			APP)
nkour's avatar
nkour committed
212
		self.window = self.xml.get_widget('change_status_message_dialog')
213
214
		if show:
			uf_show = helpers.get_uf_show(show)
215
			title_text = _('%s Status Message') % uf_show
216
		else:
217
			title_text = _('Status Message')
nkour's avatar
typo    
nkour committed
218
		self.window.set_title(title_text)
219
		
nkour's avatar
nkour committed
220
221
		message_textview = self.xml.get_widget('message_textview')
		self.message_buffer = message_textview.get_buffer()
222
223
		self.message_buffer.connect('changed',
			self.toggle_sensitiviy_of_save_as_preset)
224
225
226
		msg = None
		if show:
			msg = gajim.config.get('last_status_msg_' + show)
227
228
		if not msg:
			msg = ''
229
		msg = helpers.from_one_line(msg)
230
		self.message_buffer.set_text(msg)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
		
		# have an empty string selectable, so user can clear msg
		self.preset_messages_dict = {'': ''}
		for msg_name in gajim.config.get_per('statusmsg'):
			msg_text = gajim.config.get_per('statusmsg', msg_name, 'message')
			msg_text = helpers.from_one_line(msg_text)
			self.preset_messages_dict[msg_name] = msg_text
		sorted_keys_list = helpers.get_sorted_keys(self.preset_messages_dict)
		
		self.message_liststore = gtk.ListStore(str) # msg_name
		self.message_combobox = self.xml.get_widget('message_combobox')
		self.message_combobox.set_model(self.message_liststore)
		cellrenderertext = gtk.CellRendererText()
		self.message_combobox.pack_start(cellrenderertext, True)
		self.message_combobox.add_attribute(cellrenderertext, 'text', 0)
		for msg_name in sorted_keys_list:
			self.message_liststore.append((msg_name,))
nkour's avatar
nkour committed
248
249
250
		self.xml.signal_autoconnect(self)
		self.window.show_all()

Yann Leboulanger's avatar
Yann Leboulanger committed
251
	def run(self):
252
253
		'''Wait for OK or Cancel button to be pressed and return status messsage
		(None if users pressed Cancel or x button of WM'''
254
		rep = self.window.run()
Yann Leboulanger's avatar
Yann Leboulanger committed
255
		if rep == gtk.RESPONSE_OK:
256
			beg, end = self.message_buffer.get_bounds()
257
258
			message = self.message_buffer.get_text(beg, end).decode('utf-8')\
				.strip()
259
			msg = helpers.to_one_line(message)
260
261
			if self.show:
				gajim.config.set('last_status_msg_' + self.show, msg)
Yann Leboulanger's avatar
Yann Leboulanger committed
262
		else:
263
			message = None # user pressed Cancel button or X wm button
264
265
		self.window.destroy()
		return message
266

267
	def on_message_combobox_changed(self, widget):
268
269
270
271
		model = widget.get_model()
		active = widget.get_active()
		if active < 0:
			return None
272
		name = model[active][0].decode('utf-8')
273
		self.message_buffer.set_text(self.preset_messages_dict[name])
Yann Leboulanger's avatar
Yann Leboulanger committed
274
	
275
	def on_change_status_message_dialog_key_press_event(self, widget, event):
nkour's avatar
fix    
nkour committed
276
		if event.keyval == gtk.keysyms.Return or \
277
		event.keyval == gtk.keysyms.KP_Enter:  # catch CTRL+ENTER
278
			if (event.state & gtk.gdk.CONTROL_MASK):
279
				self.window.response(gtk.RESPONSE_OK)
nkour's avatar
nkour committed
280

281
282
283
284
285
286
	def toggle_sensitiviy_of_save_as_preset(self, widget):
		btn = self.xml.get_widget('save_as_preset_button')
		if self.message_buffer.get_char_count() == 0:
			btn.set_sensitive(False)
		else:
			btn.set_sensitive(True)
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
	
	def on_save_as_preset_button_clicked(self, widget):
		start_iter, finish_iter = self.message_buffer.get_bounds()
		status_message_to_save_as_preset = self.message_buffer.get_text(
			start_iter, finish_iter)
		dlg = InputDialog(_('Save as Preset Status Message'),
			_('Please type a name for this status message'), is_modal = True)
		response = dlg.get_response()
		if response == gtk.RESPONSE_OK:
			msg_name = dlg.input_entry.get_text()
			# user_input holds the name that the user wants for the preset message
			iter_ = self.message_liststore.append((msg_name,))
			msg_text = helpers.to_one_line(status_message_to_save_as_preset)
			gajim.config.add_per('statusmsg', msg_name)
			gajim.config.set_per('statusmsg', msg_name, 'message', msg_text)
			self.preset_messages_dict[msg_name] = msg_text
			# select in combobox the one we just saved 
			self.message_combobox.set_active_iter(iter_)

306

307
308
class AddNewContactWindow:
	'''Class for AddNewContactWindow'''
309
	def __init__(self, account, jid = None):
nkour's avatar
nkour committed
310
311
312
		self.account = account
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'add_new_contact_window', APP)
		self.window = self.xml.get_widget('add_new_contact_window')
313
314
315
316
		self.uid_entry = self.xml.get_widget('uid_entry')
		self.protocol_combobox = self.xml.get_widget('protocol_combobox')
		self.jid_entry = self.xml.get_widget('jid_entry')
		self.nickname_entry = self.xml.get_widget('nickname_entry')
nkour's avatar
nkour committed
317
318
319
320
321
322
		if len(gajim.connections) >= 2:
			prompt_text =\
_('Please fill in the data of the contact you want to add in account %s') %account
		else:
			prompt_text = _('Please fill in the data of the contact you want to add')
		self.xml.get_widget('prompt_label').set_text(prompt_text)
nkour's avatar
nkour committed
323
		self.old_uid_value = ''
nkour's avatar
nkour committed
324
		liststore = gtk.ListStore(str, str)
nkour's avatar
nkour committed
325
326
327
		liststore.append(['Jabber', ''])
		self.agents = ['Jabber']
		jid_agents = []
328
329
330
331
		for j in gajim.contacts.get_jid_list(account):
			contact = gajim.contacts.get_first_contact_from_jid(account, j)
			if _('Transports') in contact.groups and contact.show != 'offline' and\
					contact.show != 'error':
nkour's avatar
nkour committed
332
333
334
335
336
337
338
339
340
341
342
343
344
345
				jid_agents.append(j)
		for a in jid_agents:
			if a.find('aim') > -1:
				name = 'AIM'
			elif a.find('icq') > -1:
				name = 'ICQ'
			elif a.find('msn') > -1:
				name = 'MSN'
			elif a.find('yahoo') > -1:
				name = 'Yahoo!'
			else:
				name = a
			iter = liststore.append([name, a])
			self.agents.append(name)
346
347
348
		
		self.protocol_combobox.set_model(liststore)
		self.protocol_combobox.set_active(0)
nkour's avatar
nkour committed
349
350
		self.fill_jid()
		if jid:
351
			self.jid_entry.set_text(jid)
nkour's avatar
nkour committed
352
			self.uid_entry.set_sensitive(False)
nkour's avatar
nkour committed
353
354
			jid_splited = jid.split('@')
			if jid_splited[1] in jid_agents:
355
				uid = jid_splited[0].replace('%', '@')
356
				self.uid_entry.set_text(uid)
Yann Leboulanger's avatar
Yann Leboulanger committed
357
358
				self.protocol_combobox.set_active(jid_agents.index(jid_splited[1])\
					+ 1)
359
			else:
360
361
				self.uid_entry.set_text(jid)
				self.protocol_combobox.set_active(0)
362
			self.set_nickname()
363
			self.nickname_entry.grab_focus()
nkour's avatar
nkour committed
364
365
366
367

		self.group_comboboxentry = self.xml.get_widget('group_comboboxentry')
		liststore = gtk.ListStore(str)
		self.group_comboboxentry.set_model(liststore)
368
		for g in gajim.groups[account].keys():
369
			if g != _('Not in Roster') and g != _('Transports'):
nkour's avatar
nkour committed
370
371
				self.group_comboboxentry.append_text(g)

372
		if not jid_agents:
nkour's avatar
nkour committed
373
			# There are no transports, so hide the protocol combobox and label
374
375
376
377
378
379
			self.protocol_combobox.hide()
			self.protocol_combobox.set_no_show_all(True)
			protocol_label = self.xml.get_widget('protocol_label')
			protocol_label.hide()
			protocol_label.set_no_show_all(True)

380
		self.xml.signal_autoconnect(self)
381
		self.window.show_all()
Yann Leboulanger's avatar
Yann Leboulanger committed
382

383
384
385
386
	def on_add_new_contact_window_key_press_event(self, widget, event):
		if event.keyval == gtk.keysyms.Escape: # ESCAPE
			self.window.destroy()

387
	def on_cancel_button_clicked(self, widget):
nkour's avatar
nkour committed
388
		'''When Cancel button is clicked'''
Yann Leboulanger's avatar
Yann Leboulanger committed
389
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
390

391
	def on_subscribe_button_clicked(self, widget):
nkour's avatar
nkour committed
392
		'''When Subscribe button is clicked'''
393
394
		jid = self.jid_entry.get_text().decode('utf-8')
		nickname = self.nickname_entry.get_text().decode('utf-8')
395
		if not jid:
396
			return
nkour's avatar
nkour committed
397
	
398
399
400
401
		# check if jid is conform to RFC and stringprep it
		try:
			jid = helpers.parse_jid(jid)
		except helpers.InvalidFormat, s:
nkour's avatar
nkour committed
402
			pritext = _('Invalid User ID')
Yann Leboulanger's avatar
fix TB    
Yann Leboulanger committed
403
			ErrorDialog(pritext, str(s)).get_response()
404
			return
405

406
		# Check if jid is already in roster
407
		if jid in gajim.contacts.get_jid_list(self.account) and \
408
			_('Not in Roster') not in \
409
			gajim.contacts.get_first_contact_from_jid(self.account, jid).groups:
410
			ErrorDialog(_('Contact already in roster'),
411
			_('This contact is already listed in your roster.')).get_response()
412
413
			return

414
415
416
		message_buffer = self.xml.get_widget('message_textview').get_buffer()
		start_iter = message_buffer.get_start_iter()
		end_iter = message_buffer.get_end_iter()
417
		message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
418
		group = self.group_comboboxentry.child.get_text().decode('utf-8')
419
		auto_auth = self.xml.get_widget('auto_authorize_checkbutton').get_active()
420
		gajim.interface.roster.req_sub(self, jid, message, self.account,
421
			group = group, pseudo = nickname, auto_auth = auto_auth)
Yann Leboulanger's avatar
Yann Leboulanger committed
422
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
423
		
424
	def fill_jid(self):
425
426
		model = self.protocol_combobox.get_model()
		index = self.protocol_combobox.get_active()
427
		jid = self.uid_entry.get_text().decode('utf-8').strip()
428
		if index > 0: # it's not jabber but a transport
429
			jid = jid.replace('@', '%')
430
		agent = model[index][1].decode('utf-8')
431
		if agent:
432
			jid += '@' + agent
433
		self.jid_entry.set_text(jid)
434

nkour's avatar
nkour committed
435
	def on_protocol_combobox_changed(self, widget):
436
		self.fill_jid()
437
438

	def guess_agent(self):
439
		uid = self.uid_entry.get_text().decode('utf-8')
440
		model = self.protocol_combobox.get_model()
441
442
		
		#If login contains only numbers, it's probably an ICQ number
443
		if uid.isdigit():
444
			if 'ICQ' in self.agents:
445
				self.protocol_combobox.set_active(self.agents.index('ICQ'))
446
				return
447

448
	def set_nickname(self):
449
450
		uid = self.uid_entry.get_text().decode('utf-8')
		nickname = self.nickname_entry.get_text().decode('utf-8')
451
		if nickname == self.old_uid_value:
452
			self.nickname_entry.set_text(uid.split('@')[0])
453
			
454
	def on_uid_entry_changed(self, widget):
455
		uid = self.uid_entry.get_text().decode('utf-8')
456
		self.guess_agent()
457
458
459
		self.set_nickname()
		self.fill_jid()
		self.old_uid_value = uid.split('@')[0]
Yann Leboulanger's avatar
Yann Leboulanger committed
460

nkour's avatar
nkour committed
461
class AboutDialog:
nkour's avatar
nkour committed
462
	'''Class for about dialog'''
nkour's avatar
nkour committed
463
	def __init__(self):
nkour's avatar
nkour committed
464
465
		dlg = gtk.AboutDialog()
		dlg.set_name('Gajim')
466
		dlg.set_version(gajim.version)
nicfit's avatar
nicfit committed
467
		s = u'Copyright \xa9 2003-2006 Gajim Team'
nkour's avatar
nkour committed
468
		dlg.set_copyright(s)
469
		text = open('../COPYING').read()
nkour's avatar
nkour committed
470
		dlg.set_license(text)
nkour's avatar
nkour committed
471

nkour's avatar
nkour committed
472
		dlg.set_comments(_('A GTK+ jabber client'))
nkour's avatar
nkour committed
473
		dlg.set_website('http://www.gajim.org')
474

nkour's avatar
nkour committed
475
		authors = [
476
			'Current Developers:',
nkour's avatar
nkour committed
477
478
479
			'Yann Le Boulanger <asterix@lagaule.org>',
			'Nikos Kouremenos <kourem@gmail.com>',
			'Dimitur Kirov <dkirov@gmail.com>',
480
			'Travis Shirk <travis@pobox.com>',
481
			'',
nkour's avatar
nkour committed
482
483
484
			_('Read AUTHORS file for full list including past developers'),
			'',
			_('THANKS:'),
nkour's avatar
nkour committed
485
		]
nkour's avatar
nkour committed
486
487
488
489
490
491
492

		text = open('../THANKS').read()
		text_splitted = text.split('\n')
		text = '\n'.join(text_splitted[:-2]) # remove one english setence
		# and add it manually as translatable
		text += '\n' + _('Last but not least, we thank all the package maintainers') + '\n'
		authors.append(text)
nkour's avatar
nkour committed
493
		
nkour's avatar
nkour committed
494
		dlg.set_authors(authors)
nkour's avatar
nkour committed
495
496
497
		
		if gtk.pygtk_version >= (2, 8, 0) and gtk.gtk_version >= (2, 8, 0):
			dlg.props.wrap_license = True
498

nkour's avatar
nkour committed
499
		pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(
500
			gajim.DATA_DIR, 'pixmaps', 'gajim_about.png'))			
501
502

		dlg.set_logo(pixbuf)
nkour's avatar
nkour committed
503
		#here you write your name in the form Name FamilyName <someone@somewhere>
nkour's avatar
nkour committed
504
		dlg.set_translator_credits(_('translator-credits'))
nkour's avatar
nkour committed
505
		
506
507
		artists = ['Christophe Got', 'Dennis Craven', 'Guillaume Morin',
			'Membris Khan']
nkour's avatar
nkour committed
508
		dlg.set_artists(artists)
nkour's avatar
nkour committed
509

nkour's avatar
nkour committed
510
		rep = dlg.run()
511
		dlg.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
512

nkour's avatar
nkour committed
513
class Dialog(gtk.Dialog):
Yann Leboulanger's avatar
Yann Leboulanger committed
514
515
	def __init__(self, parent, title, buttons, default = None):
		gtk.Dialog.__init__(self, title, parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR)
nkour's avatar
nkour committed
516

Yann Leboulanger's avatar
Yann Leboulanger committed
517
518
519
		self.set_border_width(6)
		self.vbox.set_spacing(12)
		self.set_resizable(False)
nkour's avatar
nkour committed
520

Yann Leboulanger's avatar
Yann Leboulanger committed
521
522
		for stock, response in buttons:
			self.add_button(stock, response)
nkour's avatar
nkour committed
523

Yann Leboulanger's avatar
Yann Leboulanger committed
524
525
526
527
		if default is not None:
			self.set_default_response(default)
		else:
			self.set_default_response(buttons[-1][1])
nkour's avatar
nkour committed
528

Yann Leboulanger's avatar
Yann Leboulanger committed
529
530
531
	def get_button(self, index):
		buttons = self.action_area.get_children()
		return index < len(buttons) and buttons[index] or None
nkour's avatar
nkour committed
532

533
class HigDialog(gtk.MessageDialog):
nkour's avatar
cleanup    
nkour committed
534
	def __init__(self, parent, type, buttons, pritext, sectext):
535
536
		gtk.MessageDialog.__init__(self, parent, 
				gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
nkour's avatar
cleanup    
nkour committed
537
				type, buttons, message_format = pritext)
538

nkour's avatar
cleanup    
nkour committed
539
		self.format_secondary_text(sectext)
540
541

	def get_response(self):
542
543
544
545
		# Give focus to top vbox
		vb = self.get_children()[0].get_children()[0]
		vb.set_flags(gtk.CAN_FOCUS)
		vb.grab_focus()
546
		self.show_all()
547
		response = self.run()
548
549
550
		self.destroy()
		return response

551
class ConfirmationDialog(HigDialog):
nkour's avatar
n0thing    
nkour committed
552
	'''HIG compliant confirmation dialog.'''
553
	def __init__(self, pritext, sectext=''):
554
		HigDialog.__init__(self, None, 
555
			gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext)
556

557
class WarningDialog(HigDialog):
558
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
559
		'''HIG compliant warning dialog.'''
560
		HigDialog.__init__( self, None, 
561
562
				gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, pritext, sectext)

563
class InformationDialog(HigDialog):
564
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
565
		'''HIG compliant info dialog.'''
566
		HigDialog.__init__( self, None, 
567
568
569
570
571
572
573
574
				gtk.MESSAGE_INFO, gtk.BUTTONS_OK, pritext, sectext)
		ok_button = self.action_area.get_children()[0]
		ok_button.connect('clicked', self.on_ok_button_clicked)
		self.show_all()

	def on_ok_button_clicked(self, widget):
		self.destroy()

575
class ErrorDialog(HigDialog):
576
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
577
		'''HIG compliant error dialog.'''
578
		HigDialog.__init__( self, None, 
579
580
				gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, pritext, sectext)

581
582
583
584
585
586
class YesNoDialog(HigDialog):
	def __init__(self, pritext, sectext=''):
		'''HIG compliant YesNo dialog.'''
		HigDialog.__init__( self, None, 
				gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, pritext, sectext)

587
588
589
class ConfirmationDialogCheck(ConfirmationDialog):
	'''HIG compliant confirmation dialog with checkbutton.'''
	def __init__(self, pritext, sectext='', checktext = ''):
590
		HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
591
			pritext, sectext)
592
593

		self.set_default_response(gtk.RESPONSE_OK)
594

595
596
		ok_button = self.action_area.get_children()[0] # right to left
		ok_button.grab_focus()
597

598
		self.checkbutton = gtk.CheckButton(checktext)
599
		self.vbox.pack_start(self.checkbutton, expand = False, fill = True)
600

601
602
603
	def is_checked(self):
		''' Get active state of the checkbutton '''
		return self.checkbutton.get_active()
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627

class FTOverwriteConfirmationDialog(ConfirmationDialog):
	'''HIG compliant confirmation dialog to overwrite or resume a file transfert'''
	def __init__(self, pritext, sectext='', propose_resume=True):
		HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_CANCEL,
			pritext, sectext)

		if propose_resume:
			b = gtk.Button('', gtk.STOCK_REFRESH)
			align = b.get_children()[0]
			hbox = align.get_children()[0]
			label = hbox.get_children()[1]
			label.set_text('_Resume')
			label.set_use_underline(True)
			self.add_action_widget(b, 100)

		b = gtk.Button('', gtk.STOCK_SAVE_AS)
		align = b.get_children()[0]
		hbox = align.get_children()[0]
		label = hbox.get_children()[1]
		label.set_text('Re_place')
		label.set_use_underline(True)
		self.add_action_widget(b, 200)

628
class InputDialog:
629
	'''Class for Input dialog'''
630
631
	def __init__(self, title, label_str, input_str = None, is_modal = True,
ok_handler = None):
632
633
		# if modal is True you also need to call get_response()
		# and ok_handler won't be used
634
635
636
637
638
		xml = gtk.glade.XML(GTKGUI_GLADE, 'input_dialog', APP)
		self.dialog = xml.get_widget('input_dialog')
		label = xml.get_widget('label')
		self.input_entry = xml.get_widget('input_entry')
		self.dialog.set_title(title)
639
		label.set_markup(label_str)
640
641
		if input_str:
			self.input_entry.set_text(input_str)
642
			self.input_entry.select_region(0, -1) # select all
643
644
645
646
647
648
		
		self.is_modal = is_modal
		if not is_modal and ok_handler is not None:
			self.ok_handler = ok_handler
			okbutton = xml.get_widget('okbutton')
			okbutton.connect('clicked', self.on_okbutton_clicked)
649
650
			cancelbutton = xml.get_widget('cancelbutton')
			cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
651
652
653
			self.dialog.show_all()

	def on_okbutton_clicked(self,  widget):
nkour's avatar
nkour committed
654
		user_input = self.input_entry.get_text().decode('utf-8')
655
		self.dialog.destroy()
nkour's avatar
nkour committed
656
		self.ok_handler(user_input)
657
658
659
	
	def on_cancelbutton_clicked(self,  widget):
		self.dialog.destroy()
660
661
662
663
664

	def get_response(self):
		if self.is_modal:
			response = self.dialog.run()
			self.dialog.destroy()
665
		return response
666

nkour's avatar
nkour committed
667
class SubscriptionRequestWindow:
668
	def __init__(self, jid, text, account):
nkour's avatar
nkour committed
669
		xml = gtk.glade.XML(GTKGUI_GLADE, 'subscription_request_window', APP)
Yann Leboulanger's avatar
Yann Leboulanger committed
670
		self.window = xml.get_widget('subscription_request_window')
nkour's avatar
nkour committed
671
672
		self.jid = jid
		self.account = account
673
674
675
676
677
678
		if len(gajim.connections) >= 2:
			prompt_text = _('Subscription request for account %s from %s')\
				% (account, self.jid)
		else:
			prompt_text = _('Subscription request from %s') % self.jid
		xml.get_widget('from_label').set_text(prompt_text)
nkour's avatar
nkour committed
679
680
681
682
		xml.get_widget('message_textview').get_buffer().set_text(text)
		xml.signal_autoconnect(self)
		self.window.show_all()

683
	def on_close_button_clicked(self, widget):
Yann Leboulanger's avatar
Yann Leboulanger committed
684
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
685
		
686
	def on_authorize_button_clicked(self, widget):
687
		'''accept the request'''
688
		gajim.connections[self.account].send_authorization(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
689
		self.window.destroy()
690
		if self.jid not in gajim.contacts.get_jid_list(self.account):
691
			AddNewContactWindow(self.account, self.jid)
692
693
694

	def on_contact_info_button_clicked(self, widget):
		'''ask vcard'''
695
696
		if gajim.interface.instances[self.account]['infos'].has_key(self.jid):
			gajim.interface.instances[self.account]['infos'][self.jid].window.present()
697
		else:
698
699
700
			contact = gajim.contacts.create_contact(jid = self.jid, name='',
			groups=[], show='', status='', sub='', ask='', resource='',
			priority=5, keyID='', our_chatstate=None, chatstate=None)
701
			gajim.interface.instances[self.account]['infos'][self.jid] = \
702
703
704
705
				vcard.VcardWindow(contact, self.account)
			# Remove jabber page
			gajim.interface.instances[self.account]['infos'][self.jid].xml.\
				get_widget('information_notebook').remove_page(0)
706
			gajim.connections[self.account].request_vcard(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
707
	
708
	def on_deny_button_clicked(self, widget):
nkour's avatar
nkour committed
709
		'''refuse the request'''
710
		gajim.connections[self.account].refuse_authorization(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
711
		self.window.destroy()
nkour's avatar
nkour committed
712

713
class JoinGroupchatWindow:
714
	def __init__(self, account, server = '', room = '', nick = ''):
715
		self.account = account
716
717
		if nick == '':
			nick = gajim.nicks[self.account]
718
		if gajim.connections[account].connected < 2:
719
			ErrorDialog(_('You are not connected to the server'),
nkour's avatar
nkour committed
720
_('You can not join a group chat unless you are connected.')).get_response()
nkour's avatar
nkour committed
721
722
			raise RuntimeError, 'You must be connected to join a groupchat'

723
724
		self._empty_required_widgets = []

nkour's avatar
nkour committed
725
726
727
728
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'join_groupchat_window', APP)
		self.window = self.xml.get_widget('join_groupchat_window')
		self.xml.get_widget('server_entry').set_text(server)
		self.xml.get_widget('room_entry').set_text(room)
729
		self.xml.get_widget('nickname_entry').set_text(nick)
nkour's avatar
nkour committed
730
		self.xml.signal_autoconnect(self)
731
		gajim.interface.instances[account]['join_gc'] = self #now add us to open windows
732
		if len(gajim.connections) > 1:
733
			title = _('Join Group Chat with account %s') % account
734
		else:
735
			title = _('Join Group Chat')
736
		self.window.set_title(title)
737
738
739
740
741
742
743

		self.recently_combobox = self.xml.get_widget('recently_combobox')
		liststore = gtk.ListStore(str)
		self.recently_combobox.set_model(liststore)
		cell = gtk.CellRendererText()
		self.recently_combobox.pack_start(cell, True)
		self.recently_combobox.add_attribute(cell, 'text', 0)
744
		self.recently_groupchat = gajim.config.get('recently_groupchat').split()
745
746
		for g in self.recently_groupchat:
			self.recently_combobox.append_text(g)
747
748
		if len(self.recently_groupchat) == 0:
			self.recently_combobox.set_sensitive(False)
749
		elif server == '' and room == '':
750
751
			self.recently_combobox.set_active(0)
			self.xml.get_widget('room_entry').select_region(0, -1)
752
753
		elif room and server:
			self.xml.get_widget('join_button').grab_focus()
754

755
756
757
758
759
760
761
762
763
764
765
766
		self._server_entry = self.xml.get_widget('server_entry')
		self._room_entry = self.xml.get_widget('room_entry')
		self._nickname_entry = self.xml.get_widget('nickname_entry')
		if not self._server_entry.get_text():
			self._empty_required_widgets.append(self._server_entry)
		if not self._room_entry.get_text():
			self._empty_required_widgets.append(self._room_entry)
		if not self._nickname_entry.get_text():
			self._empty_required_widgets.append(self._nickname_entry)
		if len(self._empty_required_widgets):
			self.xml.get_widget('join_button').set_sensitive(False)

767
		self.window.show_all()
768

769
	def on_join_groupchat_window_destroy(self, widget):
nkour's avatar
nkour committed
770
		'''close window'''
771
		# remove us from open windows
772
		del gajim.interface.instances[self.account]['join_gc']
773

774
775
776
777
	def on_join_groupchat_window_key_press_event(self, widget, event):
		if event.keyval == gtk.keysyms.Escape: # ESCAPE
			widget.destroy()

778
779
780
781
782
783
784
785
786
787
	def on_required_entry_changed(self, widget):
		if not widget.get_text():
			self._empty_required_widgets.append(widget)
			self.xml.get_widget('join_button').set_sensitive(False)
		else:
			if widget in self._empty_required_widgets:
				self._empty_required_widgets.remove(widget)
				if len(self._empty_required_widgets) == 0:
					self.xml.get_widget('join_button').set_sensitive(True)

788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
	def on_room_entry_key_press_event(self, widget, event):
		# Check for pressed @ and jump to server_entry if found
		if event.keyval == gtk.keysyms.at:
			self.xml.get_widget('server_entry').grab_focus()
			return True

	def on_server_entry_key_press_event(self, widget, event):
		# If backspace is pressed in empty server_entry, return to the room entry
		backspace = event.keyval == gtk.keysyms.BackSpace
		server_entry = self.xml.get_widget('server_entry')
		empty = len(server_entry.get_text()) == 0
		if backspace and empty:
			self.xml.get_widget('room_entry').grab_focus()
			return True

803
804
805
	def on_recently_combobox_changed(self, widget):
		model = widget.get_model()
		iter = widget.get_active_iter()
806
		gid = model[iter][0].decode('utf-8')
807
808
809
		self.xml.get_widget('room_entry').set_text(gid.split('@')[0])
		self.xml.get_widget('server_entry').set_text(gid.split('@')[1])

Yann Leboulanger's avatar
Yann Leboulanger committed
810
	def on_cancel_button_clicked(self, widget):
nkour's avatar
nkour committed
811
		'''When Cancel button is clicked'''
Yann Leboulanger's avatar
Yann Leboulanger committed
812
		self.window.destroy()
813

814
	def on_join_button_clicked(self, widget):
nkour's avatar
nkour committed
815
		'''When Join button is clicked'''