Skip to content
Snippets Groups Projects
gtkgui.py 106 KiB
Newer Older
Yann Leboulanger's avatar
Yann Leboulanger committed
##	plugins/gtkgui.py
##
## Gajim Team:
Yann Leboulanger's avatar
Yann Leboulanger committed
## 	- Yann Le Boulanger <asterix@lagaule.org>
Vincent Hanquez's avatar
Vincent Hanquez committed
## 	- Vincent Hanquez <tab@snarc.org>
Yann Leboulanger's avatar
Yann Leboulanger committed
##
Yann Leboulanger's avatar
Yann Leboulanger committed
##	Copyright (C) 2003-2005 Gajim Team
Yann Leboulanger's avatar
Yann Leboulanger committed
##
## 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.
##

def usage():
	#TODO: use i18n
	print "usage :", sys.argv[0], ' [OPTION]'
	print "  -p\tport on whitch the sock plugin listen"
	print "  -h, --help\tdisplay this help and exit"

if __name__ == "__main__":
	import getopt, pickle, sys, socket
	try:
		opts, args = getopt.getopt(sys.argv[1:], "p:h", ["help"])
	except getopt.GetoptError:
		# print help information and exit:
		usage()
		sys.exit(2)
	port = 8255
	for o, a in opts:
		if o == '-p':
			port = a
		if o in ("-h", "--help"):
			usage()
			sys.exit()
	sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	try:
		sock.connect(('', 8255))
	except:
		#TODO: use i18n
		print "unable to connect to localhost on port "+str(port)
	else:
		evp = pickle.dumps(('EXEC_PLUGIN', '', 'gtkgui'))
		sock.send('<'+evp+'>')
		sock.close()
	sys.exit()

Yann Leboulanger's avatar
Yann Leboulanger committed
import pygtk
pygtk.require('2.0')
import gtk
from gtk import TRUE, FALSE
Yann Leboulanger's avatar
Yann Leboulanger committed
import gtk.glade,gobject
import os,string,time,Queue, sys
Yann Leboulanger's avatar
Yann Leboulanger committed
import common.optparser,common.sleepy
from common import i18n
_ = i18n._
APP = i18n.APP
gtk.glade.bindtextdomain (APP, i18n.DIR)
gtk.glade.textdomain (APP)
Yann Leboulanger's avatar
Yann Leboulanger committed

Yann Leboulanger's avatar
Yann Leboulanger committed
from dialogs import *
Yann Leboulanger's avatar
Yann Leboulanger committed
GTKGUI_GLADE='plugins/gtkgui/gtkgui.glade'
class ImageCellRenderer(gtk.GenericCellRenderer):

	__gproperties__ = {
		"image": (gobject.TYPE_OBJECT, "Image", 
		"Image", gobject.PARAM_READWRITE),
	}
                     
	def __init__(self):
		self.__gobject_init__()
		self.image = None

	def do_set_property(self, pspec, value):
		setattr(self, pspec.name, value)

	def do_get_property(self, pspec):
		return getattr(self, pspec.name)

	def func(self, model, path, iter, (image, tree)):
		if model.get_value(iter, 0) == image:
			self.redraw = 1
			cell_area = tree.get_cell_area(path, tree.get_column(0))
			tree.queue_draw_area(cell_area.x, cell_area.y, cell_area.width, cell_area.height)

	def animation_timeout(self, tree, image):
		if image.get_storage_type() == gtk.IMAGE_ANIMATION:
			model = tree.get_model()
			model.foreach(self.func, (image, tree))
			if self.redraw:
				gobject.timeout_add(image.get_data('iter').get_delay_time(), self.animation_timeout, tree, image)
			else:
				image.set_data('iter', None)
				
	def on_render(self, window, widget, background_area,cell_area, \
		expose_area, flags):
		pix_rect = gtk.gdk.Rectangle()
		pix_rect.x, pix_rect.y, pix_rect.width, pix_rect.height = self.on_get_size(widget, cell_area)

		pix_rect.x += cell_area.x
		pix_rect.y += cell_area.y
		pix_rect.width  -= 2 * self.get_property("xpad")
		pix_rect.height -= 2 * self.get_property("ypad")

		draw_rect = cell_area.intersect(pix_rect)
		draw_rect = expose_area.intersect(draw_rect)
		if self.image.get_storage_type() == gtk.IMAGE_ANIMATION:
			if not self.image.get_data('iter'):
				animation = self.image.get_animation()
				self.image.set_data('iter', animation.get_iter())
				gobject.timeout_add(self.image.get_data('iter').get_delay_time(), self.animation_timeout, widget, self.image)

			pix = self.image.get_data('iter').get_pixbuf()
		elif self.image.get_storage_type() == gtk.IMAGE_PIXBUF:
			pix = self.image.get_pixbuf()
		else:
			return
		window.draw_pixbuf(widget.style.black_gc, pix, \
			draw_rect.x-pix_rect.x, draw_rect.y-pix_rect.y, draw_rect.x, \
			draw_rect.y+2, draw_rect.width, draw_rect.height, \
			gtk.gdk.RGB_DITHER_NONE, 0, 0)

	def on_get_size(self, widget, cell_area):
		if self.image.get_storage_type() == gtk.IMAGE_ANIMATION:
			animation = self.image.get_animation()
			pix = animation.get_iter().get_pixbuf()
		elif self.image.get_storage_type() == gtk.IMAGE_PIXBUF:
			pix = self.image.get_pixbuf()
		else:
			return 0, 0, 0, 0
		pixbuf_width  = pix.get_width()
		pixbuf_height = pix.get_height()
		calc_width  = self.get_property("xpad") * 2 + pixbuf_width
		calc_height = self.get_property("ypad") * 2 + pixbuf_height
		x_offset = 0
		y_offset = 0
		if cell_area and pixbuf_width > 0 and pixbuf_height > 0:
			x_offset = self.get_property("xalign") * (cell_area.width - calc_width -  self.get_property("xpad"))
			y_offset = self.get_property("yalign") * (cell_area.height - calc_height -  self.get_property("ypad"))
		return x_offset, y_offset, calc_width, calc_height

gobject.type_register(ImageCellRenderer)


Yann Leboulanger's avatar
Yann Leboulanger committed
class user:
Yann Leboulanger's avatar
Yann Leboulanger committed
	"""Informations concerning each users"""
Yann Leboulanger's avatar
Yann Leboulanger committed
	def __init__(self, *args):
		if len(args) == 0:
			self.jid = ''
			self.name = ''
			self.groups = []
			self.show = ''
			self.status = ''
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.sub = ''
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.resource = ''
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.keyID = ''
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.jid = args[0]
			self.name = args[1]
			self.groups = args[2]
			self.show = args[3]
			self.status = args[4]
			self.sub = args[5]
			self.ask = args[6]
			self.resource = args[7]
			self.priority = args[8]
			self.keyID = args[9]
		else: raise TypeError, _('bad arguments')
Yann Leboulanger's avatar
Yann Leboulanger committed
class tabbed_chat_Window:
	"""Class for tabbed chat window"""
	def __init__(self, user, plugin, account):
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'tabbed_chat', APP)
		self.xml.get_widget('notebook').remove_page(0)
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.plugin = plugin
		self.account = account
		self.widgets = {}
		self.tagIn = {}
		self.tagOut = {}
		self.tagStatus = {}
		self.users = {user.jid: user}
		self.nb_unread = {user.jid: 0}
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.window = self.xml.get_widget('tabbed_chat')
		self.new_user(user)
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.show_title()
		self.xml.signal_connect('gtk_widget_destroy', self.delete_event)
		self.xml.signal_connect('on_focus', self.on_focus)
		self.xml.signal_connect('on_chat_key_press_event', \
			self.on_chat_key_press_event)
		self.xml.signal_connect('on_notebook_switch_page', \
			self.on_notebook_switch_page)
	def update_tags(self):
		for jid in self.tagIn:
			self.tagIn[jid].set_property("foreground", \
				self.plugin.config['inmsgcolor'])
			self.tagOut[jid].set_property("foreground", \
				self.plugin.config['outmsgcolor'])
			self.tagStatus[jid].set_property("foreground", \
				self.plugin.config['statusmsgcolor'])

Yann Leboulanger's avatar
Yann Leboulanger committed
	def show_title(self):
		unread = 0
		for jid in self.nb_unread:
			unread += self.nb_unread[jid]
Yann Leboulanger's avatar
Yann Leboulanger committed
		start = ""
		if unread > 1:
			start = "[" + str(unread) + "] "
		elif unread == 1:
			start = "* "
		self.window.set_title(start + "Chat (" + self.account + ")")

	def draw_widgets(self, user):
		widget_img = self.widgets[user.jid]['image_status']
Yann Leboulanger's avatar
Yann Leboulanger committed
		image = self.plugin.roster.pixbufs[user.show]
		if image.get_storage_type() == gtk.IMAGE_ANIMATION:
			widget_img.set_from_animation(image.get_animation())
		elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
			widget_img.set_from_pixbuf(image.get_pixbuf())
		self.widgets[user.jid]['button_contact'].set_label(\
			user.name + ' <' + user.jid + '>')
Yann Leboulanger's avatar
Yann Leboulanger committed
		if not user.keyID:
			self.widgets[user.jid]['toggle_gpg'].set_sensitive(False)
	def redraw_tab(self, jid):
Yann Leboulanger's avatar
Yann Leboulanger committed
		start = ''
		if self.nb_unread[jid] > 1:
			start = "[" + str(self.nb_unread[jid]) + "] "
		elif self.nb_unread[jid] == 1:
Yann Leboulanger's avatar
Yann Leboulanger committed
			start = "* "
		nb = self.xml.get_widget("notebook")
		child = self.widgets[jid]['vbox_tab']
		nb.set_tab_label_text(child, start + self.users[jid].name)
	def set_image(self, image, jid):
		if image.get_storage_type() == gtk.IMAGE_ANIMATION:
			self.widgets[jid]['image_status'].\
				set_from_animation(image.get_animation())
		elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
			self.widgets[jid]['image_status'].\
				set_from_pixbuf(image.get_pixbuf())

Yann Leboulanger's avatar
Yann Leboulanger committed
	def delete_event(self, widget):
		"""close window"""
		#clean self.plugin.windows[self.account]['chats']
		for jid in self.users:
			del self.plugin.windows[self.account]['chats'][jid]
Yann Leboulanger's avatar
Yann Leboulanger committed
		del self.plugin.windows[self.account]['chats']['tabbed']

	def get_active_jid(self):
		nb = self.xml.get_widget("notebook")
		child = nb.get_nth_page(nb.get_current_page())
		jid = ''
		for j in self.widgets:
			c = self.widgets[j]['vbox_tab']
Yann Leboulanger's avatar
Yann Leboulanger committed
	def on_clear(self, widget):
		"""When clear button is pressed :
		clear the conversation"""
		jid = self.get_active_jid()
		buffer = self.widgets[jid]['conversation'].get_buffer()
Yann Leboulanger's avatar
Yann Leboulanger committed
		deb, end = buffer.get_bounds()
		buffer.delete(deb, end)

	def on_close_clicked(self, button):
		"""When close button is pressed :
		close a tab"""
		jid = self.get_active_jid()
		if len(self.widgets) == 1:
			button.get_toplevel().destroy()
		else:
			nb = self.xml.get_widget('notebook')
			nb.remove_page(nb.get_current_page())
			del self.plugin.windows[self.account]['chats'][jid]
			del self.users[jid]
			del self.nb_unread[jid]
			del self.widgets[jid]
			del self.tagIn[jid]
			del self.tagOut[jid]
			del self.tagStatus[jid]
Yann Leboulanger's avatar
Yann Leboulanger committed
	def on_focus(self, widget, event):
		"""When window get focus"""
		jid = self.get_active_jid()
		if self.nb_unread[jid] > 0:
			self.nb_unread[jid] = 0
			self.redraw_tab(jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.show_title()
			self.plugin.systray.remove_jid(jid, self.account)
Yann Leboulanger's avatar
Yann Leboulanger committed

	def on_history(self, widget):
		"""When history button is pressed : call log window"""
		jid = self.get_active_jid()
		if not self.plugin.windows['logs'].has_key(jid):
			self.plugin.windows['logs'][jid] = log_Window(self.plugin, jid)
	def on_notebook_switch_page(self, nb, page, page_num):
		child = nb.get_nth_page(page_num)
		jid = ''
		for j in self.widgets:
			c = self.widgets[j]['vbox_tab']
			if c == child:
				jid = j
				break
		if self.nb_unread[jid] > 0:
			self.nb_unread[jid] = 0
			self.redraw_tab(jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.show_title()
			self.plugin.systray.remove_jid(jid, self.account)
		child = self.widgets[jid]['vbox_tab']
		nb = self.xml.get_widget("notebook")
		nb.set_current_page(nb.page_num(child))
		self.widgets[jid]['message'].grab_focus()

Yann Leboulanger's avatar
Yann Leboulanger committed
	def new_user(self, user):
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.nb_unread[user.jid] = 0
		self.users[user.jid] = user
		self.widgets[user.jid] = {}
		vb = gtk.VBox()
		vb.set_border_width(5)
		self.widgets[user.jid]['vbox_tab'] = vb
		hb = gtk.HBox(spacing=5)
		hb.set_border_width(5)
		vb.pack_start(hb, expand=False)
		button = gtk.Button(stock=gtk.STOCK_JUSTIFY_FILL)
		button.get_children()[0].get_children()[0].get_children()[1].set_text("History")
		button.connect("clicked", self.on_history)
		hb.pack_start(button, expand=False, fill=False)
		button = gtk.Button(stock=gtk.STOCK_CLEAR)
		button.connect("clicked", self.on_clear)
		hb.pack_start(button, expand=False, fill=False)
		hb.pack_start(img, expand=False, fill=False)
		self.widgets[user.jid]['image_status'] = img

		img = gtk.Image()
		img.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_BUTTON)
		button = gtk.ToggleButton()
		button.add(img)
		button.set_relief(gtk.RELIEF_NONE)
		hb.pack_start(button, expand=False, fill=False)
		self.widgets[user.jid]['toggle_gpg'] = button

		fixed = gtk.Fixed()
		fixed.set_size_request(20, -1)
		hb.pack_start(fixed)
		button = gtk.Button("Anonymous")
		button.set_relief(gtk.RELIEF_NONE)
		button.connect("clicked", self.on_button_contact_clicked)
		button.set_use_underline(False)
		fixed.put(button, 0, 0)
		self.widgets[user.jid]['button_contact'] = button
		
		img = gtk.Image()
		img.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON)
		button = gtk.Button()
		button.add(img)
		button.connect("clicked", self.on_close_clicked)
		hb.pack_start(button, expand=False, fill=False)

		vp = gtk.VPaned()
		vb.pack_start(vp)
		vp.set_position(170)

		sw = gtk.ScrolledWindow()
Yann Leboulanger's avatar
Yann Leboulanger committed
		sw.set_shadow_type(gtk.SHADOW_IN)
		vp.add1(sw)
		sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		tv = gtk.TextView()
		tv.set_wrap_mode(gtk.WRAP_WORD)
Yann Leboulanger's avatar
Yann Leboulanger committed
		tv.set_editable(False)
		tv.set_cursor_visible(False)
		sw.add(tv)
		self.widgets[user.jid]['conversation'] = tv
		buffer = tv.get_buffer()
		end_iter = buffer.get_end_iter()
		buffer.create_mark('end', end_iter, 0)
		self.tagIn[user.jid] = buffer.create_tag("incoming")
		color = self.plugin.config['inmsgcolor']
		self.tagIn[user.jid].set_property("foreground", color)
		self.tagOut[user.jid] = buffer.create_tag("outgoing")
		color = self.plugin.config['outmsgcolor']
		self.tagOut[user.jid].set_property("foreground", color)
		self.tagStatus[user.jid] = buffer.create_tag("status")
		color = self.plugin.config['statusmsgcolor']
		self.tagStatus[user.jid].set_property("foreground", color)
		sw = gtk.ScrolledWindow()
Yann Leboulanger's avatar
Yann Leboulanger committed
		sw.set_shadow_type(gtk.SHADOW_IN)
		vp.add2(sw)
		sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		tv = gtk.TextView()
		tv.set_wrap_mode(gtk.WRAP_WORD)
Yann Leboulanger's avatar
Yann Leboulanger committed
		sw.add(tv)
		self.widgets[user.jid]['message'] = tv
		tv.connect('key_press_event', self.on_msg_key_press_event)

		vb.show_all()
		nb = self.xml.get_widget("notebook")
		nb.set_current_page(nb.append_page(vb))

		self.redraw_tab(user.jid)
		self.draw_widgets(user)
		#print queued messages
		if self.plugin.queues[self.account].has_key(user.jid):
			self.read_queue(self.plugin.queues[self.account][user.jid])
		if user.show != 'online':
			self.print_conversation(_("%s is now %s (%s)") % (user.name, \
				user.show, user.status), user.jid, 'status')
Yann Leboulanger's avatar
Yann Leboulanger committed
	def on_msg_key_press_event(self, widget, event):
		"""When a key is pressed :
		if enter is pressed without the shit key, message (if not empty) is sent
		and printed in the conversation"""
		if event.keyval == gtk.keysyms.Return:
			if (event.state & gtk.gdk.SHIFT_MASK):
				return 0
			txt_buffer = widget.get_buffer()
			start_iter = txt_buffer.get_start_iter()
			end_iter = txt_buffer.get_end_iter()
			txt = txt_buffer.get_text(start_iter, end_iter, 0)
			if txt != '':
				keyID = ''
				jid = self.get_active_jid()
				if self.widgets[jid]['toggle_gpg'].get_active():
					keyID = self.users[jid].keyID
				self.plugin.send('MSG', self.account, (jid, txt, keyID))
Yann Leboulanger's avatar
Yann Leboulanger committed
				txt_buffer.set_text('', -1)
				self.print_conversation(txt, jid, jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
			return 1
		return 0

	def on_chat_key_press_event(self, widget, event):
		nb = self.xml.get_widget("notebook")
		st = "1234567890"
Yann Leboulanger's avatar
Yann Leboulanger committed
		if event.keyval == gtk.keysyms.Escape:
			jid = self.get_active_jid()
			if len(self.widgets) == 1:
				widget.get_toplevel().destroy()
			else:
				nb.remove_page(nb.get_current_page())
				del self.plugin.windows[self.account]['chats'][jid]
				del self.users[jid]
				del self.nb_unread[jid]
				del self.widgets[jid]
				del self.tagIn[jid]
				del self.tagOut[jid]
				del self.tagStatus[jid]
		elif event.string and event.string in st \
			and (event.state & gtk.gdk.MOD1_MASK):
			nb.set_current_page(st.index(event.string))
		elif event.keyval == gtk.keysyms.Page_Down and \
			(event.state & gtk.gdk.CONTROL_MASK):
			current = nb.get_current_page()
			if current > 0:
				nb.set_current_page(current-1)
			else:
				nb.set_current_page(nb.get_n_pages()-1)
		elif event.keyval == gtk.keysyms.Page_Up and \
			(event.state & gtk.gdk.CONTROL_MASK):
			current = nb.get_current_page()
			if current < (nb.get_n_pages()-1):
				nb.set_current_page(current+1)
			else:
				nb.set_current_page(0)
	def on_button_contact_clicked(self, widget):
		"""When button contact is clicked"""
		jid = self.get_active_jid()
		user = self.users[jid]
		self.plugin.roster.on_info(widget, user, self.account)

Yann Leboulanger's avatar
Yann Leboulanger committed
	def read_queue(self, q):
		"""read queue and print messages containted in it"""
		jid = self.get_active_jid()
		user = self.users[jid]
Yann Leboulanger's avatar
Yann Leboulanger committed
		while not q.empty():
			evt = q.get()
			self.print_conversation(evt[0], jid, tim = evt[1])
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.plugin.roster.nb_unread -= 1
		self.plugin.roster.show_title()
		del self.plugin.queues[self.account][jid]
		self.plugin.roster.redraw_jid(jid, self.account)
		self.plugin.systray.remove_jid(jid, self.account)
Yann Leboulanger's avatar
Yann Leboulanger committed
		showOffline = self.plugin.config['showoffline']
		if (user.show == 'offline' or user.show == 'error') and \
			not showOffline:
			if len(self.plugin.roster.contacts[self.account][jid]) == 1:
Yann Leboulanger's avatar
Yann Leboulanger committed
				self.plugin.roster.remove_user(user, self.account)

	def print_conversation(self, txt, jid, contact = None, tim = None):
		"""Print a line in the conversation :
		if contact is set to status : it's a status message
		if contact is set to another value : it's an outgoing message
		if contact is not set : it's an incomming message"""
		conversation = self.widgets[jid]['conversation']
		buffer = conversation.get_buffer()
Yann Leboulanger's avatar
Yann Leboulanger committed
		if not txt:
			txt = ""
		end_iter = buffer.get_end_iter()
		if not tim:
			tim = time.localtime()
		tims = time.strftime("[%H:%M:%S]", tim)
		buffer.insert(end_iter, tims + ' ')
		
		otxt = ''
		ttxt = ''
		if contact == 'status':
Yann Leboulanger's avatar
Yann Leboulanger committed
			tag = 'status'
			ttxt = txt + '\n'
		else:
			if contact:
				tag = 'outgoing'
				name = self.plugin.nicks[self.account] 
			else:
				tag = 'incoming'
				name = user.name
				
			if string.find(txt, '/me ') == 0:
				ttxt = name + ' ' + txt[4:] + '\n'
			else:
				ttxt = '<' + name + '> '
				otxt = txt + '\n'

		buffer.insert_with_tags_by_name(end_iter, ttxt, tag)
		if len(otxt) > 0:
			beg = 0
			if self.plugin.config['useemoticons']:
				index = 0
				while index < len(otxt):
					if otxt[index] in self.plugin.roster.begin_emot:
						for s in self.plugin.roster.emoticons:
							l = len(s)
							if s == otxt[index:index+l]:
								buffer.insert(end_iter, otxt[beg:index])
								buffer.insert_pixbuf(end_iter, \
									self.plugin.roster.emoticons[s])
Yann Leboulanger's avatar
Yann Leboulanger committed
								index+=l
								beg = index
					index+=1
			buffer.insert(end_iter, otxt[beg:])
		
		#scroll to the end of the textview
		conversation.scroll_to_mark(buffer.get_mark('end'), 0.1, 0, 0, 0)
		if (jid != self.get_active_jid() or not self.window.is_active()) and \
			contact != 'status':
			self.nb_unread[jid] += 1
			self.redraw_tab(jid)
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.show_title()

Yann Leboulanger's avatar
Yann Leboulanger committed
class message_Window:
	"""Class for chat window"""
Yann Leboulanger's avatar
Yann Leboulanger committed
	def delete_event(self, widget):
Yann Leboulanger's avatar
Yann Leboulanger committed
		"""close window"""
		del self.plugin.windows[self.account]['chats'][self.user.jid]
Yann Leboulanger's avatar
Yann Leboulanger committed
	def print_conversation(self, txt, jid, contact = None, tim = None):
		"""Print a line in the conversation :
		if contact is set to status : it's a status message
		if contact is set to another value : it's an outgoing message
		if contact is not set : it's an incomming message"""
		conversation_textview = self.xml.get_widget('conversation_textview')
		conversation_buffer = conversation_textview.get_buffer()
		end_iter = conversation_buffer.get_end_iter()
			tim = time.localtime()
		tims = time.strftime("[%H:%M:%S]", tim)
		conversation_buffer.insert(end_iter, tims + ' ')
		
		otxt = ''
		ttxt = ''
		if contact and contact == 'status':
			tag = 'status'
			ttxt = txt + '\n'
Yann Leboulanger's avatar
Yann Leboulanger committed
		else:
			if contact:
				tag = 'outgoing'
				name = self.plugin.nicks[self.account] 
			else:
				tag = 'incoming'
				name = self.user.name
				
			if string.find(txt, '/me ') == 0:
				ttxt = name + ' ' + txt[4:] + '\n'
			else:
				ttxt = '<' + name + '> '
				otxt = txt + '\n'

		conversation_buffer.insert_with_tags_by_name(end_iter, ttxt, tag)
		if len(otxt) > 0:
Yann Leboulanger's avatar
Yann Leboulanger committed
			beg = 0
			if self.plugin.config['useemoticons']:
				index = 0
				while index < len(otxt):
					if otxt[index] in self.plugin.roster.begin_emot:
						for s in self.plugin.roster.emoticons:
							l = len(s)
							if s == otxt[index:index+l]:
								conversation_buffer.insert(end_iter, otxt[beg:index])
								conversation_buffer.insert_pixbuf(end_iter, self.plugin.roster.emoticons[s])
Yann Leboulanger's avatar
Yann Leboulanger committed
								index+=l
								beg = index
					index+=1
			conversation_buffer.insert(end_iter, otxt[beg:])
		#scroll to the end of the textview
		conversation_textview.scroll_to_mark(conversation_buffer.get_mark('end'),\
			0.1, 0, 0, 0)
		if not self.window.is_active() and contact != 'status':
			self.nb_unread += 1
			self.show_title()

	def show_title(self):
		start = ""
		if self.nb_unread > 1:
			start = "[" + str(self.nb_unread) + "] "
		elif self.nb_unread == 1:
			start = "* "
		self.window.set_title(start + self.user.name + " (" + self.account + ")")

	def update_tags(self):
		self.tagIn.set_property("foreground", self.plugin.config['inmsgcolor'])
		self.tagOut.set_property("foreground", self.plugin.config['outmsgcolor'])
		self.tagStatus.set_property("foreground", \
			self.plugin.config['statusmsgcolor'])

	def set_image(self, image, jid):
		if image.get_storage_type() == gtk.IMAGE_ANIMATION:
			self.status_image.set_from_animation(image.get_animation())
		elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
			self.status_image.set_from_pixbuf(image.get_pixbuf())
Yann Leboulanger's avatar
Yann Leboulanger committed
	def read_queue(self, q):
		"""read queue and print messages containted in it"""
Yann Leboulanger's avatar
Yann Leboulanger committed
		while not q.empty():
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.print_conversation(evt[0], self.user.jid, tim = evt[1])
			self.plugin.roster.nb_unread -= 1
		self.plugin.roster.show_title()
		del self.plugin.queues[self.account][self.user.jid]
		self.plugin.roster.redraw_jid(self.user.jid, self.account)
		self.plugin.systray.remove_jid(self.user.jid, self.account)
		showOffline = self.plugin.config['showoffline']
		if (self.user.show == 'offline' or self.user.show == 'error') and \
			not showOffline:
			if len(self.plugin.roster.contacts[self.account][self.user.jid]) == 1:
				self.plugin.roster.remove_user(self.user, self.account)
	def on_chat_key_press_event(self, widget, event):
		if event.keyval == gtk.keysyms.Escape:
			widget.get_toplevel().destroy()

Yann Leboulanger's avatar
Yann Leboulanger committed
	def on_msg_key_press_event(self, widget, event):
		"""When a key is pressed :
		if enter is pressed without the shit key, message (if not empty) is sent
		and printed in the conversation"""
Yann Leboulanger's avatar
Yann Leboulanger committed
		if event.keyval == gtk.keysyms.Return:
			if (event.state & gtk.gdk.SHIFT_MASK):
				return 0
			message_buffer = widget.get_buffer()
			start_iter = message_buffer.get_start_iter()
			end_iter = message_buffer.get_end_iter()
			txt = message_buffer.get_text(start_iter, end_iter, 0)
Yann Leboulanger's avatar
Yann Leboulanger committed
				keyID = ''
				if self.xml.get_widget('gpg_toggle_button').get_active():
Yann Leboulanger's avatar
Yann Leboulanger committed
					keyID = self.keyID
				self.plugin.send('MSG', self.account, (self.user.jid, txt, keyID))
Yann Leboulanger's avatar
Yann Leboulanger committed
				self.print_conversation(txt, self.user.jid, self.user.jid)
				widget.grab_focus()
Yann Leboulanger's avatar
Yann Leboulanger committed
			return 1
		return 0

	def on_clear(self, widget):
		"""When clear button is pressed :
		clear the conversation"""
		conversation_buffer = self.xml.get_widget('conversation_textview').\
			get_buffer()
		deb, end = conversation_buffer.get_bounds()
		conversation_buffer.delete(deb, end)
	def on_history(self, widget):
		"""When history button is pressed : call log window"""
		if not self.plugin.windows['logs'].has_key(self.user.jid):
			self.plugin.windows['logs'][self.user.jid] = log_Window(self.plugin, self.user.jid)

	def on_focus(self, widget, event):
		"""When window get focus"""
		self.plugin.systray.remove_jid(self.user.jid, self.account)
		if self.nb_unread > 0:
			self.nb_unread = 0
			self.show_title()
	def on_button_contact_clicked(self, widget):
		"""When button contact is clicked"""
		self.plugin.roster.on_info(widget, self.user, self.account)

	def __init__(self, user, plugin, account):
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.user = user
		self.plugin = plugin
		self.account = account
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.keyID = self.user.keyID
		self.xml = gtk.glade.XML(GTKGUI_GLADE, 'Chat', APP)
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.window = self.xml.get_widget('Chat')
		self.status_image = self.xml.get_widget('status_image')
		image = self.plugin.roster.pixbufs[user.show]
		if image.get_storage_type() == gtk.IMAGE_ANIMATION:
			self.status_image.set_from_animation(image.get_animation())
		elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
			self.status_image.set_from_pixbuf(image.get_pixbuf())
		contact_button = self.xml.get_widget('contact_button')
		contact_button.set_label(user.name + ' <'	+ user.jid + '>')
Yann Leboulanger's avatar
Yann Leboulanger committed
		if not self.keyID:
			self.xml.get_widget('gpg_toggle_button').set_sensitive(False)
		message_textview = self.xml.get_widget('message_textview')
		message_textview.grab_focus()
		conversation_textview = self.xml.get_widget('conversation_textview')
		conversation_buffer = conversation_textview.get_buffer()
		end_iter = conversation_buffer.get_end_iter()
		conversation_buffer.create_mark('end', end_iter, 0)
		self.xml.signal_connect('gtk_widget_destroy', self.delete_event)
Yann Leboulanger's avatar
Yann Leboulanger committed
		self.xml.signal_connect('on_clear_clicked', self.on_clear)
		self.xml.signal_connect('on_button_contact_clicked', \
			self.on_button_contact_clicked)
		self.xml.get_widget('button_contact').set_use_underline(False)
		self.xml.signal_connect('on_focus', self.on_focus)
		self.xml.signal_connect('on_history_clicked', self.on_history)
		self.xml.signal_connect('on_msg_key_press_event', \
			self.on_msg_key_press_event)
		self.xml.signal_connect('on_chat_key_press_event', \
			self.on_chat_key_press_event)
		self.tagIn = conversation_buffer.create_tag("incoming")
		color = self.plugin.config['inmsgcolor']
		self.tagOut = conversation_buffer.create_tag("outgoing")
		color = self.plugin.config['outmsgcolor']
		self.tagStatus = conversation_buffer.create_tag("status")
		color = self.plugin.config['statusmsgcolor']
		#print queued messages
		if plugin.queues[account].has_key(user.jid):
			self.read_queue(plugin.queues[account][user.jid])
		if self.user.show != 'online':
Yann Leboulanger's avatar
Yann Leboulanger committed
			self.print_conversation(_("%s is now %s (%s)") % (user.name, \
				user.show, user.status), user.jid, 'status')
class gc:
	def delete_event(self, widget):
		"""close window"""
		self.plugin.send('GC_STATUS', self.account, (self.nick, self.jid,\
			'offline', 'offline'))
		del self.plugin.windows[self.account]['gc'][self.jid]

	def on_close(self, widget):
		"""When Cancel button is clicked"""
		widget.get_toplevel().destroy()

	def get_role_iter(self, name):
		model = self.tree.get_model()
		iter = model.get_iter_root()
		if not iter:
			return None
		while not fin:
			account_name = model.get_value(iter, 1)
			if name == account_name:
				return iter
			iter = model.iter_next(iter)
			if not iter:
				fin = True
		return None

	def get_user_iter(self, jid):
		model = self.tree.get_model()
		fin = False
		role = model.get_iter_root()
		if not role:
			return None
		while not fin:
			fin2 = False
			user = model.iter_children(role)
			if not user:
				fin2=True
			while not fin2:
				if jid == model.get_value(user, 1):
					return user
				user = model.iter_next(user)
				if not user:
					fin2 = True
			role = model.iter_next(role)
			if not role:
				fin = True

	def remove_user(self, nick):
		"""Remove a user from the roster"""
		model = self.tree.get_model()
		iter = self.get_user_iter(nick)
		parent_iter = model.iter_parent(iter)
		model.remove(iter)
		if model.iter_n_children(parent_iter) == 0:
			model.remove(parent_iter)
	def add_user_to_roster(self, nick, show, role):
		model = self.tree.get_model()
		img = self.plugin.roster.pixbufs[show]
		role_iter = self.get_role_iter(role)
		if not role_iter:
			role_iter = model.append(None, (self.plugin.roster.pixbufs['closed']\
				, role))
		iter = model.append(role_iter, (img, nick))
		self.tree.expand_row((model.get_path(role_iter)), False)
		return iter
	
	def get_role(self, jid_iter):
		model = self.tree.get_model()
		path = model.get_path(jid_iter)[0]
		iter = model.get_iter(path)
		return model.get_value(iter, 1)
	def chg_user_status(self, nick, show, status, role, affiliation, jid, \
		reason, actor, statusCode, account):
		"""When a user change his status"""
		model = self.tree.get_model()
		if show == 'offline' or show == 'error':
			if statusCode == '307':
				self.print_conversation(_("%s has been kicked by %s: %s") % (nick, \
Yann Leboulanger's avatar
Yann Leboulanger committed
					jid, actor, reason))
			self.remove_user(nick)
			iter = self.get_user_iter(nick)
			if not iter:
				iter = self.add_user_to_roster(nick, show, role)
				actual_role = self.get_role(iter)
				if role != actual_role:
					self.remove_user(nick)
					self.add_user_to_roster(nick, show, role)
				else:
					img = self.plugin.roster.pixbufs[show]
					model.set_value(iter, 0, img)
	def on_msg_key_press_event(self, widget, event):
		"""When a key is pressed :
		if enter is pressed without the shit key, message (if not empty) is sent
		and printed in the conversation"""
		if event.keyval == gtk.keysyms.Return:
			if (event.state & gtk.gdk.SHIFT_MASK):
				return 0
			txt_buffer = widget.get_buffer()
			start_iter = txt_buffer.get_start_iter()
			end_iter = txt_buffer.get_end_iter()
			txt = txt_buffer.get_text(start_iter, end_iter, 0)
			if txt != '':
				self.plugin.send('GC_MSG', self.account, (self.jid, txt))
				txt_buffer.set_text('', -1)
				widget.grab_focus()
			return 1
		return 0

Yann Leboulanger's avatar
Yann Leboulanger committed
	def print_conversation(self, txt, jid, contact = None, tim = None):
		"""Print a line in the conversation :
		if contact is set : it's a message from someone
		if contact is not set : it's a message from the server"""
		conversation = self.xml.get_widget('conversation')
		buffer = conversation.get_buffer()
		if not txt:
			txt = ""
		end_iter = buffer.get_end_iter()
		if not tim:
			tim = time.localtime()
		tims = time.strftime("[%H:%M:%S]", tim)
		buffer.insert(end_iter, tims)
			if contact == self.nick:
				buffer.insert_with_tags_by_name(end_iter, '<'+contact+'> ', \
					'outgoing')
				buffer.insert_with_tags_by_name(end_iter, '<' + contact + '> ', \
					'incoming')
			buffer.insert(end_iter, txt+'\n')
		else:
			buffer.insert_with_tags_by_name(end_iter, txt+'\n', \
				'status')
		#scroll to the end of the textview
		conversation.scroll_to_mark(buffer.get_mark('end'), 0.1, 0, 0, 0)

	def kick(self, widget, room_jid, nick):
		"""kick a user"""
		self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'none'))

	def grant_voice(self, widget, room_jid, nick):
		"""grant voice privilege to a user"""
		self.plugin.send('SET_ROLE', self.account, (room_jid, nick, \
			'participant'))

	def revoke_voice(self, widget, room_jid, nick):
		"""revoke voice privilege to a user"""
		self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'visitor'))

	def grant_moderator(self, widget, room_jid, nick):
		"""grant moderator privilege to a user"""
		self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'moderator'))

	def revoke_moderator(self, widget, room_jid, nick):
		"""revoke moderator privilege to a user"""
		self.plugin.send('SET_ROLE', self.account, (room_jid, nick, \
			'participant'))

	def ban(self, widget, room_jid, nick):
		"""ban a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'outcast'))

	def grant_membership(self, widget, room_jid, nick):
		"""grant membership privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'member'))

	def revoke_membership(self, widget, room_jid, nick):
		"""revoke membership privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'none'))

	def grant_admin(self, widget, room_jid, nick):
		"""grant administrative privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'admin'))

	def revoke_admin(self, widget, room_jid, nick):
		"""revoke administrative privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'member'))

	def grant_owner(self, widget, room_jid, nick):
		"""grant owner privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'owner'))

	def revoke_owner(self, widget, room_jid, nick):
		"""revoke owner privilege to a user"""
		self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
			'admin'))

	def mk_menu(self, event, iter):
		"""Make user's popup menu"""
		model = self.tree.get_model()
		nick = model.get_value(iter, 1)
		
		menu = gtk.Menu()
		item = gtk.MenuItem(_("MUC"))
		menu.append(item)
		
		menu_sub = gtk.Menu()
		item.set_submenu(menu_sub)
		item = gtk.MenuItem(_("Kick"))
		menu_sub.append(item)
		item.connect("activate", self.kick, self.jid, nick)
		item = gtk.MenuItem(_("Grant voice"))
		menu_sub.append(item)
		item.connect("activate", self.grant_voice, self.jid, nick)
		item = gtk.MenuItem(_("Revoke voice"))
		menu_sub.append(item)
		item.connect("activate", self.revoke_voice, self.jid, nick)
		item = gtk.MenuItem(_("Grant moderator"))
		menu_sub.append(item)