dialogs.py 56.7 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
##	dialogs.py
Yann Leboulanger's avatar
Yann Leboulanger committed
3
##
4
5
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
6
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
7
8
9
## 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
10
11
12
13
14
15
16
17
18
19
20
21
##
## 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
22
23
import gtk.glade
import gobject
Yann Leboulanger's avatar
Yann Leboulanger committed
24
import os
25
import sys
26

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

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

nkour's avatar
nkour committed
37
# those imports are not used in this file, but in files that 'import dialogs'
38
# so they can do dialog.GajimThemesWindow() for example
39
from filetransfers_window import FileTransfersWindow
40
from gajim_themes_window import GajimThemesWindow
41
from advanced import AdvancedConfigurationWindow
42

43
from common import gajim
44
from common import helpers
45
from common import i18n
46

47
48
49
50
_ = i18n._
APP = i18n.APP
gtk.glade.bindtextdomain (APP, i18n.DIR)
gtk.glade.textdomain (APP)
Yann Leboulanger's avatar
Yann Leboulanger committed
51

52
GTKGUI_GLADE = 'gtkgui.glade'
Yann Leboulanger's avatar
Yann Leboulanger committed
53

54
class EditGroupsDialog:
nkour's avatar
nkour committed
55
	'''Class for the edit group dialog window'''
56
	def __init__(self, user, account):
57
58
59
60
		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
61
		self.changes_made = False
62
		self.list = self.xml.get_widget('groups_treeview')
63
		self.xml.get_widget('nickname_label').set_markup(
64
			_("Contact's name: <i>%s</i>") % user.get_shown_name())
65
		self.xml.get_widget('jid_label').set_markup(
66
			_('JID: <i>%s</i>') % user.jid)
67
		
68
69
70
71
		self.xml.signal_autoconnect(self)
		self.init_list()

	def run(self):
72
		self.dialog.show_all()
73
		if self.changes_made:
nkour's avatar
nkour committed
74
			gajim.connections[self.account].update_contact(self.user.jid,
75
				self.user.name, self.user.groups)
76

77
78
79
80
	def on_edit_groups_dialog_response(self, widget, response_id):
		if response_id == gtk.RESPONSE_CLOSE:
			self.dialog.destroy()

nkour's avatar
nkour committed
81
	def update_contact(self):
82
		tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
83
84
85
86
87
88
89
		if not tag:
			gajim.interface.roster.remove_contact(self.user, self.account)
			gajim.interface.roster.add_contact_to_roster(self.user.jid,
				self.account)
			gajim.connections[self.account].update_contact(self.user.jid,
				self.user.name, self.user.groups)
			return
90
91
92
93
94
95
96
97
98
99
100
101
102
		all_jid = gajim.contacts.get_metacontacts_jids(tag)
		for _account in all_jid:
			for _jid in all_jid[_account]:
				c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
				if not c:
					continue
				gajim.interface.roster.remove_contact(c, _account)
				gajim.interface.roster.add_contact_to_roster(_jid, _account)
				gajim.connections[_account].update_contact(_jid, c.name, c.groups)

	def remove_group(self, group):
		'''add group group to self.user and all his brothers'''
		tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
103
104
105
106
		if not tag:
			if group in self.user.groups:
				self.user.groups.remove(group)
			return
107
108
109
110
111
112
113
114
115
116
117
		all_jid = gajim.contacts.get_metacontacts_jids(tag)
		for _account in all_jid:
			for _jid in all_jid[_account]:
				contacts = gajim.contacts.get_contact(_account, _jid)
				for contact in contacts:
					if group in contact.groups:
						contact.groups.remove(group)

	def add_group(self, group):
		'''add group group to self.user and all his brothers'''
		tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
118
119
120
121
		if not tag:
			if group not in self.user.groups:
				self.user.groups.append(group)
			return
122
123
124
125
126
127
128
		all_jid = gajim.contacts.get_metacontacts_jids(tag)
		for _account in all_jid:
			for _jid in all_jid[_account]:
				contacts = gajim.contacts.get_contact(_account, _jid)
				for contact in contacts:
					if not group in contact.groups:
						contact.groups.append(group)
129
130

	def on_add_button_clicked(self, widget):
131
		group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
132
133
134
135
136
137
		if not group:
			return
		# check if it already exists
		model = self.list.get_model()
		iter = model.get_iter_root()
		while iter:
138
			if model.get_value(iter, 0).decode('utf-8') == group:
139
140
				return
			iter = model.iter_next(iter)
141
		self.changes_made = True
142
		model.append((group, True))
143
		self.add_group(group)
nkour's avatar
nkour committed
144
		self.update_contact()
145
146

	def group_toggled_cb(self, cell, path):
147
		self.changes_made = True
148
149
		model = self.list.get_model()
		model[path][1] = not model[path][1]
150
151
152
153
154
		group = model[path][0].decode('utf-8')
		if model[path][1]:
			self.add_group(group)
		else:
			self.remove_group(group)
nkour's avatar
nkour committed
155
		self.update_contact()
156
157

	def init_list(self):
158
		store = gtk.ListStore(str, bool)
159
		self.list.set_model(store)
160
		for g in gajim.groups[self.account].keys():
161
			if g in gajim.special_groups:
162
				continue
163
164
165
166
167
168
169
			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
170
		column.set_expand(True)
171
172
173
		self.list.append_column(column)
		renderer = gtk.CellRendererText()
		column.pack_start(renderer)
174
		column.set_attributes(renderer, text = 0)
175
176
		
		column = gtk.TreeViewColumn(_('In the group'))
nkour's avatar
nkour committed
177
		column.set_expand(False)
178
179
180
181
182
		self.list.append_column(column)
		renderer = gtk.CellRendererToggle()
		column.pack_start(renderer)
		renderer.set_property('activatable', True)
		renderer.connect('toggled', self.group_toggled_cb)
183
		column.set_attributes(renderer, active = 1)
184

185
class PassphraseDialog:
nkour's avatar
nkour committed
186
	'''Class for Passphrase dialog'''
Yann Leboulanger's avatar
Yann Leboulanger committed
187
	def run(self):
nkour's avatar
nkour committed
188
		'''Wait for OK button to be pressed and return passphrase/password'''
189
		rep = self.window.run()
Yann Leboulanger's avatar
Yann Leboulanger committed
190
		if rep == gtk.RESPONSE_OK:
191
			passphrase = self.passphrase_entry.get_text().decode('utf-8')
Yann Leboulanger's avatar
Yann Leboulanger committed
192
		else:
193
194
195
196
			passphrase = -1
		save_passphrase_checkbutton = self.xml.\
			get_widget('save_passphrase_checkbutton')
		self.window.destroy()
197
		return passphrase, save_passphrase_checkbutton.get_active()
198

199
	def __init__(self, titletext, labeltext, checkbuttontext):
200
201
202
203
		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
204
		self.window.set_title(titletext)
205
		self.xml.get_widget('message_label').set_text(labeltext)
206
207
		self.xml.get_widget('save_passphrase_checkbutton').set_label(
			checkbuttontext)
208
		self.xml.signal_autoconnect(self)
209
		self.window.show_all()
Yann Leboulanger's avatar
Yann Leboulanger committed
210

211
class ChooseGPGKeyDialog:
nkour's avatar
nkour committed
212
	'''Class for GPG key dialog'''
213
	def __init__(self, title_text, prompt_text, secret_keys, selected = None):
214
		#list : {keyID: userName, ...}
215
216
		xml = gtk.glade.XML(GTKGUI_GLADE, 'choose_gpg_key_dialog', APP)
		self.window = xml.get_widget('choose_gpg_key_dialog')
217
		self.window.set_title(title_text)
218
		self.keys_treeview = xml.get_widget('keys_treeview')
219
220
		prompt_label = xml.get_widget('prompt_label')
		prompt_label.set_text(prompt_text)
221
		model = gtk.ListStore(str, str)
222
		model.set_sort_column_id(1, gtk.SORT_ASCENDING)
223
		self.keys_treeview.set_model(model)
224
225
		#columns
		renderer = gtk.CellRendererText()
226
		self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
227
			renderer, text = 0)
228
		renderer = gtk.CellRendererText()
nkour's avatar
nkour committed
229
		self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
230
			renderer, text = 1)
231
		self.fill_tree(secret_keys, selected)
232
233
		self.window.show_all()

234
235
236
	def run(self):
		rep = self.window.run()
		if rep == gtk.RESPONSE_OK:
237
238
			selection = self.keys_treeview.get_selection()
			(model, iter) = selection.get_selected()
nkour's avatar
nkour committed
239
240
			keyID = [ model[iter][0].decode('utf-8'),
				model[iter][1].decode('utf-8') ]
241
242
		else:
			keyID = None
243
		self.window.destroy()
244
245
		return keyID

246
247
248
249
250
251
252
253
254
	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)


255
class ChangeStatusMessageDialog:
256
	def __init__(self, show = None):
257
		self.show = show
258
259
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'change_status_message_dialog',
			APP)
nkour's avatar
nkour committed
260
		self.window = self.xml.get_widget('change_status_message_dialog')
261
262
		if show:
			uf_show = helpers.get_uf_show(show)
263
			title_text = _('%s Status Message') % uf_show
264
		else:
265
			title_text = _('Status Message')
nkour's avatar
typo    
nkour committed
266
		self.window.set_title(title_text)
267
		
nkour's avatar
nkour committed
268
269
		message_textview = self.xml.get_widget('message_textview')
		self.message_buffer = message_textview.get_buffer()
270
271
		self.message_buffer.connect('changed',
			self.toggle_sensitiviy_of_save_as_preset)
272
273
274
		msg = None
		if show:
			msg = gajim.config.get('last_status_msg_' + show)
275
276
		if not msg:
			msg = ''
277
		msg = helpers.from_one_line(msg)
278
		self.message_buffer.set_text(msg)
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
		
		# 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
296
297
298
		self.xml.signal_autoconnect(self)
		self.window.show_all()

Yann Leboulanger's avatar
Yann Leboulanger committed
299
	def run(self):
300
301
		'''Wait for OK or Cancel button to be pressed and return status messsage
		(None if users pressed Cancel or x button of WM'''
302
		rep = self.window.run()
Yann Leboulanger's avatar
Yann Leboulanger committed
303
		if rep == gtk.RESPONSE_OK:
304
			beg, end = self.message_buffer.get_bounds()
305
306
			message = self.message_buffer.get_text(beg, end).decode('utf-8')\
				.strip()
307
			msg = helpers.to_one_line(message)
308
309
			if self.show:
				gajim.config.set('last_status_msg_' + self.show, msg)
Yann Leboulanger's avatar
Yann Leboulanger committed
310
		else:
311
			message = None # user pressed Cancel button or X wm button
312
313
		self.window.destroy()
		return message
314

315
	def on_message_combobox_changed(self, widget):
316
317
318
319
		model = widget.get_model()
		active = widget.get_active()
		if active < 0:
			return None
320
		name = model[active][0].decode('utf-8')
321
		self.message_buffer.set_text(self.preset_messages_dict[name])
Yann Leboulanger's avatar
Yann Leboulanger committed
322
	
323
	def on_change_status_message_dialog_key_press_event(self, widget, event):
nkour's avatar
fix    
nkour committed
324
		if event.keyval == gtk.keysyms.Return or \
325
		event.keyval == gtk.keysyms.KP_Enter:  # catch CTRL+ENTER
326
			if (event.state & gtk.gdk.CONTROL_MASK):
327
				self.window.response(gtk.RESPONSE_OK)
nkour's avatar
nkour committed
328

329
330
331
332
333
334
	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)
335
336
337
338
339
340
341
342
343
344
345
	
	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()
			msg_text = helpers.to_one_line(status_message_to_save_as_preset)
346
347
348
349
			if not msg_name: # msg_name was ''
				msg_name = msg_text
			iter_ = self.message_liststore.append((msg_name,))
			
350
351
352
353
354
355
			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_)

356

357
358
class AddNewContactWindow:
	'''Class for AddNewContactWindow'''
359
	def __init__(self, account, jid = None):
nkour's avatar
nkour committed
360
361
362
		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')
363
364
365
366
		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
367
368
369
370
371
372
		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
373
		self.old_uid_value = ''
nkour's avatar
nkour committed
374
		liststore = gtk.ListStore(str, str)
nkour's avatar
nkour committed
375
376
377
		liststore.append(['Jabber', ''])
		self.agents = ['Jabber']
		jid_agents = []
378
379
380
381
		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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
				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)
396
397
398
		
		self.protocol_combobox.set_model(liststore)
		self.protocol_combobox.set_active(0)
nkour's avatar
nkour committed
399
400
		self.fill_jid()
		if jid:
401
			self.jid_entry.set_text(jid)
nkour's avatar
nkour committed
402
			self.uid_entry.set_sensitive(False)
nkour's avatar
nkour committed
403
404
			jid_splited = jid.split('@')
			if jid_splited[1] in jid_agents:
405
				uid = jid_splited[0].replace('%', '@')
406
				self.uid_entry.set_text(uid)
Yann Leboulanger's avatar
Yann Leboulanger committed
407
408
				self.protocol_combobox.set_active(jid_agents.index(jid_splited[1])\
					+ 1)
409
			else:
410
411
				self.uid_entry.set_text(jid)
				self.protocol_combobox.set_active(0)
412
			self.set_nickname()
413
			self.nickname_entry.grab_focus()
nkour's avatar
nkour committed
414
415
416
417

		self.group_comboboxentry = self.xml.get_widget('group_comboboxentry')
		liststore = gtk.ListStore(str)
		self.group_comboboxentry.set_model(liststore)
418
		for g in gajim.groups[account].keys():
419
			if g not in gajim.special_groups:
nkour's avatar
nkour committed
420
421
				self.group_comboboxentry.append_text(g)

422
		if not jid_agents:
nkour's avatar
nkour committed
423
			# There are no transports, so hide the protocol combobox and label
424
425
426
427
428
429
			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)

430
		self.xml.signal_autoconnect(self)
431
		self.window.show_all()
Yann Leboulanger's avatar
Yann Leboulanger committed
432

433
434
435
436
	def on_add_new_contact_window_key_press_event(self, widget, event):
		if event.keyval == gtk.keysyms.Escape: # ESCAPE
			self.window.destroy()

437
	def on_cancel_button_clicked(self, widget):
nkour's avatar
nkour committed
438
		'''When Cancel button is clicked'''
Yann Leboulanger's avatar
Yann Leboulanger committed
439
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
440

441
	def on_subscribe_button_clicked(self, widget):
nkour's avatar
nkour committed
442
		'''When Subscribe button is clicked'''
443
444
		jid = self.jid_entry.get_text().decode('utf-8')
		nickname = self.nickname_entry.get_text().decode('utf-8')
445
		if not jid:
446
			return
nkour's avatar
nkour committed
447
	
448
449
450
451
		# 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
452
			pritext = _('Invalid User ID')
Yann Leboulanger's avatar
fix TB    
Yann Leboulanger committed
453
			ErrorDialog(pritext, str(s)).get_response()
454
			return
455

456
457
458
459
460
461
		# No resource in jid
		if jid.find('/') >= 0:
			pritext = _('Invalid User ID')
			ErrorDialog(pritext, _('The user ID must not contain a resource.')).get_response()
			return

462
		# Check if jid is already in roster
463
464
465
466
467
468
		if jid in gajim.contacts.get_jid_list(self.account):
			c = gajim.contacts.get_first_contact_from_jid(self.account, jid)
			if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
				ErrorDialog(_('Contact already in roster'),
				_('This contact is already listed in your roster.')).get_response()
				return
469

470
471
472
		message_buffer = self.xml.get_widget('message_textview').get_buffer()
		start_iter = message_buffer.get_start_iter()
		end_iter = message_buffer.get_end_iter()
473
		message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
474
		group = self.group_comboboxentry.child.get_text().decode('utf-8')
475
		auto_auth = self.xml.get_widget('auto_authorize_checkbutton').get_active()
476
		gajim.interface.roster.req_sub(self, jid, message, self.account,
477
			group = group, pseudo = nickname, auto_auth = auto_auth)
Yann Leboulanger's avatar
Yann Leboulanger committed
478
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
479
		
480
	def fill_jid(self):
481
482
		model = self.protocol_combobox.get_model()
		index = self.protocol_combobox.get_active()
483
		jid = self.uid_entry.get_text().decode('utf-8').strip()
484
		if index > 0: # it's not jabber but a transport
485
			jid = jid.replace('@', '%')
486
		agent = model[index][1].decode('utf-8')
487
		if agent:
488
			jid += '@' + agent
489
		self.jid_entry.set_text(jid)
490

nkour's avatar
nkour committed
491
	def on_protocol_combobox_changed(self, widget):
492
		self.fill_jid()
493
494

	def guess_agent(self):
495
		uid = self.uid_entry.get_text().decode('utf-8')
496
		model = self.protocol_combobox.get_model()
497
498
		
		#If login contains only numbers, it's probably an ICQ number
499
		if uid.isdigit():
500
			if 'ICQ' in self.agents:
501
				self.protocol_combobox.set_active(self.agents.index('ICQ'))
502
				return
503

504
	def set_nickname(self):
505
506
		uid = self.uid_entry.get_text().decode('utf-8')
		nickname = self.nickname_entry.get_text().decode('utf-8')
507
		if nickname == self.old_uid_value:
508
			self.nickname_entry.set_text(uid.split('@')[0])
509
			
510
	def on_uid_entry_changed(self, widget):
511
		uid = self.uid_entry.get_text().decode('utf-8')
512
		self.guess_agent()
513
514
515
		self.set_nickname()
		self.fill_jid()
		self.old_uid_value = uid.split('@')[0]
Yann Leboulanger's avatar
Yann Leboulanger committed
516

nkour's avatar
nkour committed
517
class AboutDialog:
nkour's avatar
nkour committed
518
	'''Class for about dialog'''
nkour's avatar
nkour committed
519
	def __init__(self):
nkour's avatar
nkour committed
520
521
		dlg = gtk.AboutDialog()
		dlg.set_name('Gajim')
522
		dlg.set_version(gajim.version)
523
		s = u'Copyright © 2003-2006 Gajim Team'
nkour's avatar
nkour committed
524
		dlg.set_copyright(s)
525
		text = open('../COPYING').read()
nkour's avatar
nkour committed
526
		dlg.set_license(text)
nkour's avatar
nkour committed
527

nkour's avatar
nkour committed
528
		dlg.set_comments(_('A GTK+ jabber client'))
nkour's avatar
nkour committed
529
		dlg.set_website('http://www.gajim.org')
530

nkour's avatar
nkour committed
531
		authors = [
532
			'Current Developers:',
nkour's avatar
nkour committed
533
534
535
			'Yann Le Boulanger <asterix@lagaule.org>',
			'Nikos Kouremenos <kourem@gmail.com>',
			'Dimitur Kirov <dkirov@gmail.com>',
536
			'Travis Shirk <travis@pobox.com>',
537
			'',
538
539
			_('Past Developers:'),
			'Vincent Hanquez <tab@snarc.org>',
nkour's avatar
nkour committed
540
541
			'',
			_('THANKS:'),
nkour's avatar
nkour committed
542
		]
nkour's avatar
nkour committed
543
544
545
546
547

		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
548
549
		text += '\n%s\n' % _('Last but not least, we would like to thank all '
			'the package maintainers.')
nkour's avatar
nkour committed
550
		authors.append(text)
nkour's avatar
nkour committed
551
		
nkour's avatar
nkour committed
552
		dlg.set_authors(authors)
nkour's avatar
nkour committed
553
554
555
		
		if gtk.pygtk_version >= (2, 8, 0) and gtk.gtk_version >= (2, 8, 0):
			dlg.props.wrap_license = True
556

nkour's avatar
nkour committed
557
		pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(
558
			gajim.DATA_DIR, 'pixmaps', 'gajim_about.png'))			
559
560

		dlg.set_logo(pixbuf)
nkour's avatar
nkour committed
561
		#here you write your name in the form Name FamilyName <someone@somewhere>
nkour's avatar
nkour committed
562
		dlg.set_translator_credits(_('translator-credits'))
nkour's avatar
nkour committed
563
		
564
565
		artists = ['Anders Ström', 'Christophe Got', 'Dennis Craven',
			'Guillaume Morin', 'Membris Khan']
nkour's avatar
nkour committed
566
		dlg.set_artists(artists)
nkour's avatar
nkour committed
567

nkour's avatar
nkour committed
568
		rep = dlg.run()
569
		dlg.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
570

nkour's avatar
nkour committed
571
class Dialog(gtk.Dialog):
Yann Leboulanger's avatar
Yann Leboulanger committed
572
573
	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
574

Yann Leboulanger's avatar
Yann Leboulanger committed
575
576
577
		self.set_border_width(6)
		self.vbox.set_spacing(12)
		self.set_resizable(False)
nkour's avatar
nkour committed
578

Yann Leboulanger's avatar
Yann Leboulanger committed
579
580
		for stock, response in buttons:
			self.add_button(stock, response)
nkour's avatar
nkour committed
581

Yann Leboulanger's avatar
Yann Leboulanger committed
582
583
584
585
		if default is not None:
			self.set_default_response(default)
		else:
			self.set_default_response(buttons[-1][1])
nkour's avatar
nkour committed
586

Yann Leboulanger's avatar
Yann Leboulanger committed
587
588
589
	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
590

591
class HigDialog(gtk.MessageDialog):
nkour's avatar
cleanup    
nkour committed
592
	def __init__(self, parent, type, buttons, pritext, sectext):
593
594
		gtk.MessageDialog.__init__(self, parent, 
				gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
nkour's avatar
cleanup    
nkour committed
595
				type, buttons, message_format = pritext)
596

nkour's avatar
cleanup    
nkour committed
597
		self.format_secondary_text(sectext)
598
599

	def get_response(self):
600
601
602
603
		# Give focus to top vbox
		vb = self.get_children()[0].get_children()[0]
		vb.set_flags(gtk.CAN_FOCUS)
		vb.grab_focus()
604
		self.show_all()
605
		response = self.run()
606
607
608
		self.destroy()
		return response

609
class ConfirmationDialog(HigDialog):
nkour's avatar
n0thing    
nkour committed
610
	'''HIG compliant confirmation dialog.'''
611
	def __init__(self, pritext, sectext=''):
612
		HigDialog.__init__(self, None, 
613
			gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext)
614

615
class WarningDialog(HigDialog):
616
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
617
		'''HIG compliant warning dialog.'''
618
		HigDialog.__init__( self, None, 
619
620
				gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, pritext, sectext)

621
class InformationDialog(HigDialog):
622
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
623
		'''HIG compliant info dialog.'''
624
		HigDialog.__init__( self, None, 
625
626
627
628
629
630
631
632
				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()

633
class ErrorDialog(HigDialog):
634
	def __init__(self, pritext, sectext=''):
nkour's avatar
n0thing    
nkour committed
635
		'''HIG compliant error dialog.'''
636
		HigDialog.__init__( self, None, 
637
638
				gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, pritext, sectext)

639
640
641
642
643
644
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)

645
646
647
class ConfirmationDialogCheck(ConfirmationDialog):
	'''HIG compliant confirmation dialog with checkbutton.'''
	def __init__(self, pritext, sectext='', checktext = ''):
648
		HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
649
			pritext, sectext)
650
651

		self.set_default_response(gtk.RESPONSE_OK)
652

653
654
		ok_button = self.action_area.get_children()[0] # right to left
		ok_button.grab_focus()
655

656
		self.checkbutton = gtk.CheckButton(checktext)
657
		self.vbox.pack_start(self.checkbutton, expand = False, fill = True)
658

659
660
661
	def is_checked(self):
		''' Get active state of the checkbutton '''
		return self.checkbutton.get_active()
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685

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)

686
class InputDialog:
687
	'''Class for Input dialog'''
688
689
	def __init__(self, title, label_str, input_str = None, is_modal = True,
ok_handler = None):
690
691
		# if modal is True you also need to call get_response()
		# and ok_handler won't be used
692
693
694
695
696
		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)
697
		label.set_markup(label_str)
698
699
		if input_str:
			self.input_entry.set_text(input_str)
700
			self.input_entry.select_region(0, -1) # select all
701
702
703
704
705
706
		
		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)
707
708
			cancelbutton = xml.get_widget('cancelbutton')
			cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
709
710
711
			self.dialog.show_all()

	def on_okbutton_clicked(self,  widget):
nkour's avatar
nkour committed
712
		user_input = self.input_entry.get_text().decode('utf-8')
713
		self.dialog.destroy()
nkour's avatar
nkour committed
714
		self.ok_handler(user_input)
715
716
717
	
	def on_cancelbutton_clicked(self,  widget):
		self.dialog.destroy()
718
719
720
721
722

	def get_response(self):
		if self.is_modal:
			response = self.dialog.run()
			self.dialog.destroy()
723
		return response
724

nkour's avatar
nkour committed
725
class SubscriptionRequestWindow:
726
	def __init__(self, jid, text, account):
nkour's avatar
nkour committed
727
		xml = gtk.glade.XML(GTKGUI_GLADE, 'subscription_request_window', APP)
Yann Leboulanger's avatar
Yann Leboulanger committed
728
		self.window = xml.get_widget('subscription_request_window')
nkour's avatar
nkour committed
729
730
		self.jid = jid
		self.account = account
731
732
733
734
735
736
		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
737
738
739
740
		xml.get_widget('message_textview').get_buffer().set_text(text)
		xml.signal_autoconnect(self)
		self.window.show_all()

741
	def on_close_button_clicked(self, widget):
Yann Leboulanger's avatar
Yann Leboulanger committed
742
		self.window.destroy()
Yann Leboulanger's avatar
Yann Leboulanger committed
743
		
744
	def on_authorize_button_clicked(self, widget):
745
		'''accept the request'''
746
		gajim.connections[self.account].send_authorization(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
747
		self.window.destroy()
748
		if self.jid not in gajim.contacts.get_jid_list(self.account):
749
			AddNewContactWindow(self.account, self.jid)
750
751
752

	def on_contact_info_button_clicked(self, widget):
		'''ask vcard'''
753
754
		if gajim.interface.instances[self.account]['infos'].has_key(self.jid):
			gajim.interface.instances[self.account]['infos'][self.jid].window.present()
755
		else:
756
757
758
			contact = gajim.contacts.create_contact(jid = self.jid, name='',
			groups=[], show='', status='', sub='', ask='', resource='',
			priority=5, keyID='', our_chatstate=None, chatstate=None)
759
			gajim.interface.instances[self.account]['infos'][self.jid] = \
760
761
762
763
				vcard.VcardWindow(contact, self.account)
			# Remove jabber page
			gajim.interface.instances[self.account]['infos'][self.jid].xml.\
				get_widget('information_notebook').remove_page(0)
764
			gajim.connections[self.account].request_vcard(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
765
	
766
	def on_deny_button_clicked(self, widget):
nkour's avatar
nkour committed
767
		'''refuse the request'''
768
		gajim.connections[self.account].refuse_authorization(self.jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
769
		self.window.destroy()
nkour's avatar
nkour committed
770

771
class JoinGroupchatWindow:
772
	def __init__(self, account, server = '', room = '', nick = ''):
773
		self.account = account
774
775
		if nick == '':
			nick = gajim.nicks[self.account]
776
		if gajim.connections[account].connected < 2:
777
			ErrorDialog(_('You are not connected to the server'),
nkour's avatar
nkour committed
778
_('You can not join a group chat unless you are connected.')).get_response()
nkour's avatar
nkour committed
779
780
			raise RuntimeError, 'You must be connected to join a groupchat'

781
782
		self._empty_required_widgets = []

nkour's avatar
nkour committed
783
784
785
786
		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)
787
		self.xml.get_widget('nickname_entry').set_text(nick)
nkour's avatar
nkour committed
788
		self.xml.signal_autoconnect(self)
789
		gajim.interface.instances[account]['join_gc'] = self #now add us to open windows
790
		if len(gajim.connections) > 1:
791
			title = _('Join Group Chat with account %s') % account
792
		else:
793
			title = _('Join Group Chat')
794
		self.window.set_title(title)
795
796
797
798
799
800
801

		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)
802
		self.recently_groupchat = gajim.config.get('recently_groupchat').split()
803
804
		for g in self.recently_groupchat:
			self.recently_combobox.append_text(g)
805
806
		if len(self.recently_groupchat) == 0:
			self.recently_combobox.set_sensitive(False)
807
		elif server == '' and room == '':
808
809
			self.recently_combobox.set_active(0)
			self.xml.get_widget('room_entry').select_region(0, -1)
810
811
		elif room and server:
			self.xml.get_widget('join_button').grab_focus()
812

813
814
815
816
817
818
819
820
821
822
823
824
		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)

825
		self.window.show_all()
826

827
	def on_join_groupchat_window_destroy(self, widget):
nkour's avatar
nkour committed
828
		'''close window'''
829
		# remove us from open windows
830
		del gajim.interface.instances[self.account]['join_gc'