Skip to content
Snippets Groups Projects
Commit c0f51fdd authored by Vincent Hanquez's avatar Vincent Hanquez
Browse files

Initial revision

parents
No related branches found
No related tags found
No related merge requests found
import hub
import jabber
import plugin
import xmlstream
#!/usr/bin/env python
## common/hub.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 Queue
import common.plugin
import common.thread
""" Hub definitions """
class GajimHub:
def __init__(self):
self.queues = {}
""" {event1:[queue1, queue2]} """
self.events = {'NOTIFY':[], 'MSG':[], 'ROSTER':[]}
self.queueIn = self.newQueue('in', 100)
# END __init__
def newQueue(self, name, size):
""" Creates a new queue """
qu = Queue.Queue(size)
self.queues[name] = qu
return qu
# END newQueue
def newPlugin(self, name):
"""Creates a new Plugin """
qu = self.newQueue(name, 100)
pl = common.plugin.GajimPlugin(name, qu, self.queueIn)
return pl
# END newPlugin
def register(self, name, event):
""" Records a plugin from an event """
qu = self.queues[name]
self.events[event].append(qu)
# END register
def sendPlugin(self, event, data):
""" Sends an event to registered plugins
NOTIFY : ('NOTIFY', (user, status, message))
MSG : ('MSG', (user, msg))
ROSTER : ('ROSTER', {jid:{'Online':_, 'Status':_, 'Show':_} ,jid:{}})"""
if self.events.has_key(event):
for i in self.events[event]:
i.put((event, data))
# END sendPlugin
# END GajimHub
This diff is collapsed.
#!/usr/bin/env python
## common/optparser.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 ConfigParser
import logging
log = logging.getLogger('common.options')
class OptionsParser:
def __init__(self, fname):
self.__fname = fname
# END __init__
def parseCfgFile(self):
try:
self.__fd = open(self.__fname)
except:
print 'error cannot open file %s\n' % (self.__fname);
return
self.__config = ConfigParser.ConfigParser()
self.__config.readfp(self.__fd)
self.__sections = self.__config.sections()
for section in self.__sections:
for option in self.__config.options(section):
value = self.__config.get(section, option, 1)
setattr(self, str(section) + '_' + \
str(option), value)
# END parseCfgFile
def __str__(self):
return "OptionsParser"
# END __str__
def __getattr__(self, attr):
if attr.startswith('__') and attr in self.__dict__.keys():
return self.__dict__[attr]
else:
for key in self.__dict__.keys():
if key == attr:
return self.__dict__[attr]
return None
# END __getattr__
def writeCfgFile(self):
try:
self.__config.write(open(self.__fname, 'w'))
except:
log.debug("Can't write config %s" % self.__fname)
return 0
return 1
# END writeCfgFile
def stop(self):
return self.writeCfgFile()
# END stop
# END OptionsParser
#!/usr/bin/env python
## common/plugin.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 common.thread
""" Plugin definitions """
class GajimPlugin:
def __init__(self, name, queueIn, queueOut):
""" queueIn is a queue to interact from the hub to the plugin """
self.name = name
self.queueIn = queueIn
self.queueOut= queueOut
# END __init__
def load(self):
self.thr = common.thread.GajimThread(self.name, self.queueIn, \
self.queueOut)
# END load
# END GajimPlugin
#!/usr/bin/env python
## common/thread.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 threading
import socket
import sys
import time
import plugins
class GajimThread(threading.Thread):
def __init__(self, name = None, queueIn = None, queueOut = None):
self.queueIn = queueIn
self.queueOut = queueOut
threading.Thread.__init__(self, target = self.run, \
name = name, args = () )
self.start()
# END __init__
def run(self):
if self.getName() == 'gtkgui':
plugins.gtkgui.plugin(self.queueIn, self.queueOut)
# END run
# END GajimThread
## xmlstream.py
##
## Copyright (C) 2001 Matthew Allum
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published
## by the Free Software Foundation; either version 2, or (at your option)
## any later version.
##
## 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 Lesser General Public License for more details.
"""\
xmlstream.py provides simple functionality for implementing
XML stream based network protocols. It is used as a base
for jabber.py.
xmlstream.py manages the network connectivity and xml parsing
of the stream. When a complete 'protocol element' ( meaning a
complete child of the xmlstreams root ) is parsed the dipatch
method is called with a 'Node' instance of this structure.
The Node class is a very simple XML DOM like class for
manipulating XML documents or 'protocol elements' in this
case.
"""
# $Id: xmlstream.py,v 1.26 2003/02/20 10:22:33 shire Exp $
import site
site.encoding = 'UTF-8'
import time, sys, re, socket
from select import select
from string import split,find,replace,join
import xml.parsers.expat
VERSION = 0.3
False = 0
True = 1
TCP = 1
STDIO = 0
TCP_SSL = 2
ENCODING = site.encoding
BLOCK_SIZE = 1024 ## Number of bytes to get at at time via socket
## transactions
def XMLescape(txt):
"Escape XML entities"
txt = replace(txt, "&", "&amp;")
txt = replace(txt, "<", "&lt;")
txt = replace(txt, ">", "&gt;")
return txt
def XMLunescape(txt):
"Unescape XML entities"
txt = replace(txt, "&lt;", "<")
txt = replace(txt, "&gt;", ">")
txt = replace(txt, "&amp;", "&")
return txt
class error:
def __init__(self, value):
self.value = str(value)
def __str__(self):
return self.value
class Node:
"""A simple XML DOM like class"""
def __init__(self, tag='', parent=None, attrs=None ):
bits = split(tag)
if len(bits) == 1:
self.name = tag
self.namespace = ''
else:
self.namespace, self.name = bits
if attrs is None:
self.attrs = {}
else:
self.attrs = attrs
self.data = []
self.kids = []
self.parent = parent
def setParent(self, node):
"Set the nodes parent node."
self.parent = node
def getParent(self):
"return the nodes parent node."
return self.parent
def getName(self):
"Set the nodes tag name."
return self.name
def setName(self,val):
"Set the nodes tag name."
self.name = val
def putAttr(self, key, val):
"Add a name/value attribute to the node."
self.attrs[key] = val
def getAttr(self, key):
"Get a value for the nodes named attribute."
try: return self.attrs[key]
except: return None
def putData(self, data):
"Set the nodes textual data"
self.data.append(data)
def insertData(self, data):
"Set the nodes textual data"
self.data.append(data)
def getData(self):
"Return the nodes textual data"
return join(self.data, '')
def getDataAsParts(self):
"Return the node data as an array"
return self.data
def getNamespace(self):
"Returns the nodes namespace."
return self.namespace
def setNamespace(self, namespace):
"Set the nodes namespace."
self.namespace = namespace
def insertTag(self, name):
""" Add a child tag of name 'name' to the node.
Returns the newly created node.
"""
newnode = Node(tag=name, parent=self)
self.kids.append(newnode)
return newnode
def insertNode(self, node):
"Add a child node to the node"
self.kids.append(node)
return node
def insertXML(self, xml_str):
"Add raw xml as a child of the node"
newnode = NodeBuilder(xml_str).getDom()
self.kids.append(newnode)
return newnode
def __str__(self):
return self._xmlnode2str()
def _xmlnode2str(self, parent=None):
"""Returns an xml ( string ) representation of the node
and it children"""
s = "<" + self.name
if self.namespace:
if parent and parent.namespace != self.namespace:
s = s + " xmlns = '%s' " % self.namespace
for key in self.attrs.keys():
val = str(self.attrs[key])
s = s + " %s='%s'" % ( key, XMLescape(val) )
s = s + ">"
cnt = 0
if self.kids != None:
for a in self.kids:
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
s = s + a._xmlnode2str(parent=self)
cnt=cnt+1
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
s = s + "</" + self.name + ">"
return s
def getTag(self, name):
"""Returns a child node with tag name. Returns None
if not found."""
for node in self.kids:
if node.getName() == name:
return node
return None
def getTags(self, name):
"""Like getTag but returns a list with matching child nodes"""
nodes=[]
for node in self.kids:
if node.getName() == name:
nodes.append(node)
return nodes
def getChildren(self):
"""Returns a nodes children"""
return self.kids
class NodeBuilder:
"""builds a 'minidom' from data parsed to it. Primarily for insertXML
method of Node"""
def __init__(self,data):
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
self._parser.StartElementHandler = self.unknown_starttag
self._parser.EndElementHandler = self.unknown_endtag
self._parser.CharacterDataHandler = self.handle_data
self.__depth = 0
self.__done = 0 #needed ?
self.__space_regex = re.compile('^\s+$')
self._parser.Parse(data,1)
def unknown_starttag(self, tag, attrs):
self.__depth = self.__depth + 1
if self.__depth == 1:
self._mini_dom = Node(tag=tag, attrs=attrs)
self._ptr = self._mini_dom
elif self.__depth > 1:
self._ptr.kids.append(Node(tag =tag,
parent=self._ptr,
attrs =attrs ))
self._ptr = self._ptr.kids[-1]
else: ## fix this ....
pass
def unknown_endtag(self, tag ):
self.__depth = self.__depth - 1
if self.__depth == 0:
self.dispatch(self._mini_dom)
elif self.__depth > 0:
self._ptr = self._ptr.parent
else:
pass
def handle_data(self, data):
if not self.__space_regex.match(data): ## check its not all blank
self._ptr.data.append(data)
def dispatch(self,dom):
self.__done = 1
def getDom(self):
return self._mini_dom
class Stream:
def __init__(
self, host, port, namespace,
debug=True,
log=None,
sock=None,
id=None,
connection=TCP
):
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
self._parser.StartElementHandler = self._unknown_starttag
self._parser.EndElementHandler = self._unknown_endtag
self._parser.CharacterDataHandler = self._handle_data
self._host = host
self._port = port
self._namespace = namespace
self.__depth = 0
self._sock = sock
self._sslObj = None
self._sslIssuer = None
self._sslServer = None
self._incomingID = None
self._outgoingID = id
self._debug = debug
self._connection=connection
self.DEBUG("stream init called")
if log:
if type(log) is type(""):
try:
self._logFH = open(log,'w')
except:
print "ERROR: can open %s for writing"
sys.exit(0)
else: ## assume its a stream type object
self._logFH = log
else:
self._logFH = None
self._timestampLog = True
def timestampLog(self,timestamp):
""" Enable or disable the showing of a timestamp in the log.
By default, timestamping is enabled.
"""
self._timestampLog = timestamp
def DEBUG(self,txt):
if self._debug:
try:
sys.stderr.write("DEBUG: %s\n" % txt)
except:
# unicode strikes again ;)
s=u''
for i in range(len(txt)):
if ord(txt[i]) < 128:
c = txt[i]
else:
c = '?'
s=s+c
sys.stderr.write("DEBUG: %s\n" % s )
def getSocket(self):
return self._sock
def header(self):
self.DEBUG("stream: sending initial header")
str = u"<?xml version='1.0' encoding='UTF-8' ?> \
<stream:stream to='%s' xmlns='%s'" % ( self._host,
self._namespace )
if self._outgoingID: str = str + " id='%s' " % self._outgoingID
str = str + " xmlns:stream='http://etherx.jabber.org/streams'>"
self.write (str)
self.read()
def _handle_data(self, data):
"""XML Parser callback"""
self.DEBUG("data-> " + data)
## TODO: get rid of empty space
## self._ptr.data = self._ptr.data + data
self._ptr.data.append(data)
def _unknown_starttag(self, tag, attrs):
"""XML Parser callback"""
self.__depth = self.__depth + 1
self.DEBUG("DEPTH -> %i , tag -> %s, attrs -> %s" % \
(self.__depth, tag, str(attrs)) )
if self.__depth == 2:
self._mini_dom = Node(tag=tag, attrs=attrs)
self._ptr = self._mini_dom
elif self.__depth > 2:
self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs))
self._ptr = self._ptr.kids[-1]
else: ## it the stream tag:
if attrs.has_key('id'):
self._incomingID = attrs['id']
def _unknown_endtag(self, tag ):
"""XML Parser callback"""
self.__depth = self.__depth - 1
self.DEBUG("DEPTH -> %i" % self.__depth)
if self.__depth == 1:
self.dispatch(self._mini_dom)
elif self.__depth > 1:
self._ptr = self._ptr.parent
else:
self.DEBUG("*** Server closed connection ? ****")
def dispatch(self, nodes, depth = 0):
"""Overide with the method you want to called with
a node structure of a 'protocol element."""
padding = ' '
padding = padding * depth
depth = depth + 1
for n in nodes:
if n.kids != None:
self.dispatch(n.kids, depth)
##def syntax_error(self, message):
## self.DEBUG("error " + message)
def _do_read( self, action, buff_size ):
"""workhorse for read() method.
added 021231 by jaclu"""
data=''
data_in = action(buff_size)
while data_in:
data = data + data_in
if len(data_in) != buff_size:
break
data_in = action(buff_size)
return data
def read(self):
"""Reads incoming data. Called by process() so nonblocking
changed 021231 by jaclu
"""
if self._connection == TCP:
raw_data = self._do_read(self._sock.recv, BLOCK_SIZE)
elif self._connection == TCP_SSL:
raw_data = self._do_read(self._sslObj.read, BLOCK_SIZE)
elif self._connection == STDIO:
raw_data = self._do_read(self.stdin.read, 1024)
else:
raw_data = '' # should never get here
# just encode incoming data once!
data = unicode(raw_data,'utf-8').encode(ENCODING,'replace')
self.DEBUG("got data %s" % data )
self.log(data, 'RECV:')
self._parser.Parse(data)
return data
def write(self,raw_data=u''):
"""Writes raw outgoing data. blocks
changed 021231 by jaclu, added unicode encoding
"""
if type(raw_data) == type(u''):
data_out = raw_data.encode('utf-8','replace')
else:
# since not suplied as unicode, we must guess at
# what the data is, iso-8859-1 seems reasonable.
# To avoid this auto assumption,
# send your data as a unicode string!
data_out = unicode(raw_data,'iso-8859-1').encode(ENCODING,'replace')
try:
if self._connection == TCP:
self._sock.send (data_out)
elif self._connection == TCP_SSL:
self._sslObj.write(data_out)
elif self._connection == STDIO:
self.stdout.write(data_out)
else:
pass
self.log(data_out, 'SENT:')
self.DEBUG("sent %s" % data_out)
except:
self.DEBUG("xmlstream write threw error")
self.disconnected()
def process(self,timeout):
reader=Node
if self._connection == TCP:
reader = self._sock
elif self._connection == TCP_SSL:
reader = self._sock
elif self._connection == STDIO:
reader = sys.stdin
else:
pass
ready_for_read,ready_for_write,err = \
select( [reader],[],[],timeout)
for s in ready_for_read:
if s == reader:
if not len(self.read()): # length of 0 means disconnect
## raise error("network error") ?
self.disconnected()
return False
return True
return False
def disconnect(self):
"""Close the stream and socket"""
self.write ( "</stream:stream>" )
self._sock.close()
self._sock = None
def disconnected(self): ## To be overidden ##
"""Called when a Network Error or disconnection occurs.
Designed to be overidden"""
self.DEBUG("Network Disconnection")
pass
def log(self, data, inout=''):
"""Logs data to the specified filehandle. Data is time stamped
and prefixed with inout"""
if self._logFH is not None:
if self._timestampLog:
self._logFH.write("%s - %s - %s\n" % (time.asctime(), inout, data))
else:
self._logFH.write("%s - %s\n" % (inout, data ) )
self._logFH.flush()
def getIncomingID(self):
"""Returns the streams ID"""
return self._incomingID
def getOutgoingID(self):
"""Returns the streams ID"""
return self._incomingID
class Client(Stream):
def connect(self):
"""Attempt to connect to specified host"""
self.DEBUG("client connect called to %s %s type %i" % (self._host,
self._port,
self._connection) )
## TODO: check below that stdin/stdout are actually open
if self._connection == STDIO: return
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self._sock.connect((self._host, self._port))
except socket.error, e:
self.DEBUG("socket error")
raise error(e)
if self._connection == TCP_SSL:
try:
self.DEBUG("Attempting to create ssl socket")
self._sslObj = socket.ssl( self._sock, None, None )
self._sslIssuer = self._sslObj.issuer()
self._sslServer = self._sslObj.server()
except:
self.DEBUG("Socket Error: No SSL Support")
raise error("No SSL Support")
self.DEBUG("connected")
self.header()
return 0
class Server:
def now(self): return time.ctime(time.time())
def __init__(self, maxclients=10):
self.host = ''
self.port = 5222
self.streams = []
# make main sockets for accepting new client requests
self.mainsocks, self.readsocks, self.writesocks = [], [], []
self.portsock = socket(AF_INET, SOCK_STREAM)
self.portsock.bind((self.host, self.port))
self.portsock.listen(maxclients)
self.mainsocks.append(self.portsock) # add to main list to identify
self.readsocks.append(self.portsock) # add to select inputs list
# event loop: listen and multiplex until server process killed
def serve(self):
print 'select-server loop starting'
while 1:
print "LOOPING"
readables, writeables, exceptions = select(self.readsocks,
self.writesocks, [])
for sockobj in readables:
if sockobj in self. mainsocks: # for ready input sockets
newsock, address = sockobj.accept() # accept not block
print 'Connect:', address, id(newsock)
self.readsocks.append(newsock)
self._makeNewStream(newsock)
# add to select list, wait
else:
# client socket: read next line
data = sockobj.recv(1024)
# recv should not block
print '\tgot', data, 'on', id(sockobj)
if not data: # if closed by the clients
sockobj.close() # close here and remv from
self.readsocks.remove(sockobj)
else:
# this may block: should really select for writes too
sockobj.send('Echo=>%s' % data)
def _makeNewStream(self, sckt):
new_stream = Stream('localhost', 5222,
'jabber:client',
sock=sckt)
self.streams.append(new_stream)
## maybe overide for a 'server stream'
new_stream.header()
return new_stream
def _getStreamSockets(self):
socks = [];
for s in self.streams:
socks.append(s.getSocket())
return socks
def _getStreamFromSocket(self, sock):
for s in self.streams:
if s.getSocket() == sock:
return s
return None
what to do when a queue is full
si un server deconne, se connecter a un autre
* Gui :
calcule de idle time
10/10/2003 23h39 : premier passage online
12/10/2003 02h23 : affichage de la contact list
[Server]
hostname = SERVER HOSTNAME
[Profile]
name = LOGIN NAME
password = PASSWORD
ressource = gajim
import gtkgui
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="Gajim">
<property name="visible">True</property>
<property name="title" translatable="yes">Gajim</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">100</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<signal name="destroy" handler="gtk_main_quit" last_modification_time="Wed, 24 Sep 2003 20:54:02 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkHandleBox" id="handlebox2">
<property name="visible">True</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="handle_position">GTK_POS_LEFT</property>
<property name="snap_edge">GTK_POS_TOP</property>
<child>
<widget class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
<widget class="GtkMenuItem" id="gajim">
<property name="visible">True</property>
<property name="label" translatable="yes">_Gajim</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="gajim_menu">
<child>
<widget class="GtkMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="quit">
<property name="visible">True</property>
<property name="label">gtk-quit</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_quit_activate" last_modification_time="Fri, 03 Oct 2003 12:49:50 GMT"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="help">
<property name="visible">True</property>
<property name="label" translatable="yes">_?</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="help_menu">
<child>
<widget class="GtkMenuItem" id="about">
<property name="visible">True</property>
<property name="label" translatable="yes">_About</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_about_activate" last_modification_time="Fri, 03 Oct 2003 12:49:50 GMT"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHandleBox" id="handlebox1">
<property name="visible">True</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="handle_position">GTK_POS_LEFT</property>
<property name="snap_edge">GTK_POS_TOP</property>
<child>
<widget class="GtkToolbar" id="toolbar1">
<property name="visible">True</property>
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
<property name="tooltips">True</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<signal name="button_press_event" handler="on_treeview_event" last_modification_time="Tue, 30 Sep 2003 09:11:17 GMT"/>
<signal name="row_activated" handler="on_row_activated" last_modification_time="Sun, 12 Oct 2003 18:11:52 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkOptionMenu" id="optionmenu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="history">0</property>
<child>
<widget class="GtkMenu" id="menu_status">
<child>
<widget class="GtkMenuItem" id="online">
<property name="visible">True</property>
<property name="label" translatable="yes">Online</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="away">
<property name="visible">True</property>
<property name="label" translatable="yes">Away</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="na">
<property name="visible">True</property>
<property name="label" translatable="yes">NA</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="dnd">
<property name="visible">True</property>
<property name="label" translatable="yes">DND</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="invisible">
<property name="visible">True</property>
<property name="label" translatable="yes">Invisible</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="offline">
<property name="visible">True</property>
<property name="label" translatable="yes">Offline</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 20:00:58 GMT"/>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="Chat">
<property name="visible">True</property>
<property name="title" translatable="yes">Chat</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">400</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<signal name="destroy" handler="gtk_widget_destroy" last_modification_time="Sun, 12 Oct 2003 18:17:05 GMT"/>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">button1</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">button2</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label_contact">
<property name="visible">True</property>
<property name="label" translatable="yes">contact</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkVPaned" id="vpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">190</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="conversation">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_NONE</property>
<property name="cursor_visible">False</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
<property name="text" translatable="yes"></property>
</widget>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="message">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
<property name="text" translatable="yes"></property>
<signal name="key_press_event" handler="on_msg_key_press_event" last_modification_time="Mon, 13 Oct 2003 17:48:48 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>
#!/usr/bin/env python
## plugins/gtkgui.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 pygtk
pygtk.require('2.0')
import gtk
import gtk.glade
import gobject
class user:
def __init__(self, *args):
if len(args) == 0:
self.name = ''
self.group = ''
self.show = ''
self.status = ''
elif len(args) == 4:
self.name = args[0]
self.group = args[1]
self.show = args[2]
self.status = args[3]
elif ((len(args)) and (type (args[0]) == type (self)) and
(self.__class__ == args[0].__class__)):
self.name = args[0].name
self.group = args[0].group
self.show = args[0].show
self.status = args[0].status
else: raise TypeError, 'bad arguments'
class message:
def delete_event(self, widget):
del self.roster.tab_messages[self.jid]
self.window.destroy()
def print_conversation(self, txt, contact = None):
txt_buffer = self.conversation.get_buffer()
end_iter = txt_buffer.get_end_iter()
if contact: who = 'moi'
else: who = 'lui'
txt_buffer.insert(end_iter, '<'+who+'> '+txt+'\n', -1)
def on_msg_key_press_event(self, widget, event):
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)
self.roster.queueOUT.put(('MSG',(self.jid, txt)))
txt_buffer.set_text('', -1)
self.print_conversation(txt, self.jid)
widget.grab_focus()
return 1
return 0
def __init__(self, jid, roster):
self.jid = jid
self.roster = roster
self.xml = gtk.glade.XML('plugins/gtkgui.glade', 'Chat')
self.window = self.xml.get_widget('Chat')
self.window.set_title('Chat with ' + jid)
self.message = self.xml.get_widget('message')
self.conversation = self.xml.get_widget('conversation')
self.window.show()
self.xml.signal_connect('gtk_widget_destroy', self.delete_event)
self.xml.signal_connect('on_msg_key_press_event', self.on_msg_key_press_event)
class roster:
def get_icon_pixbuf(self, stock):
return self.tree.render_icon(stock, size = gtk.ICON_SIZE_MENU, detail = None)
def mkl_group(self):
self.l_group = []
for u in self.l_contact:
if u.group in self.l_group:
pass
else:
self.l_group.append(u.group)
def mkroster(self):
self.treestore.clear()
for g in self.l_group:
iter_g = self.treestore.append(None, (self.pixbufs['online'], g, 'group'))
for c in self.l_contact:
if c.group == g:
# print c.status
self.treestore.append(iter_g, (self.pixbufs[c.show], c.name, c.show))
# if c.status == 'Online':
# self.treestore.append(iter_g, (self.pixbufs['Online'], c.name, 'Online'))
# elif c.status == None:
# self.treestore.append(iter_g, (self.pixbufs['away'], c.name, 'away'))
def mkroster2(self, tab):
self.l_contact = []
for jid in tab.keys():
user1 = user(jid, 'general', tab[jid]["Show"], tab[jid]["Status"])
self.l_contact.append(user1)
self.mkl_group()
self.mkroster()
self.tree.collapse_row((0,3))
def update_iter(self, widget, path, iter, data):
val = self.treestore.get_value(iter, 1)
if val == data[0]:
self.treestore.set_value(iter, 0, self.pixbufs[data[1]])
def chg_status(self, jid, show, status):
for u in self.l_contact:
if u.name == jid:
u.show = show
u.status = status
self.treestore.foreach(self.update_iter, (jid, show))
def mk_menu_c(self, event):
self.menu_c = gtk.Menu()
item = gtk.MenuItem("user1")
self.menu_c.append(item)
item = gtk.MenuItem("user2")
self.menu_c.append(item)
item = gtk.MenuItem("user3")
self.menu_c.append(item)
self.menu_c.popup(None, None, None, event.button, event.time)
self.menu_c.show_all()
def mk_menu_g(self, event):
self.menu_c = gtk.Menu()
item = gtk.MenuItem("grp1")
self.menu_c.append(item)
item = gtk.MenuItem("grp2")
self.menu_c.append(item)
item = gtk.MenuItem("grp3")
self.menu_c.append(item)
self.menu_c.popup(None, None, None, event.button, event.time)
self.menu_c.show_all()
def on_treeview_event(self, widget, event):
if (event.button == 3) & (event.type == gtk.gdk.BUTTON_PRESS):
try:
path, column, x, y = self.tree.get_path_at_pos(int(event.x), int(event.y))
except TypeError:
return
iter = self.treestore.get_iter(path)
data = self.treestore.get_value(iter, 2)
if data == 'group':
self.mk_menu_g(event)
else:
self.mk_menu_c(event)
return gtk.TRUE
return gtk.FALSE
def on_status_changed(self, widget):
self.queueOUT.put(('STATUS',widget.name))
def on_quit(self, widget):
self.queueOUT.put(('QUIT',''))
gtk.mainquit()
def on_row_activated(self, widget, path, col):
iter = self.treestore.get_iter(path)
jid = self.treestore.get_value(iter, 1)
if self.tab_messages.has_key(jid):
#NE FONCTIONNE PAS !
self.tab_messages[jid].window.grab_focus()
else:
self.tab_messages[jid]=message(jid, self)
def __init__(self, queueOUT):
#initialisation des variables
# FIXME : handle no file ...
self.xml = gtk.glade.XML('plugins/gtkgui.glade', 'Gajim')
self.tree = self.xml.get_widget('treeview')
self.treestore = gtk.TreeStore(gtk.gdk.Pixbuf, str, str)
add_pixbuf = self.get_icon_pixbuf(gtk.STOCK_ADD)
remove_pixbuf = self.get_icon_pixbuf(gtk.STOCK_REMOVE)
self.pixbufs = {"online":add_pixbuf, "away":remove_pixbuf, "xa":remove_pixbuf, "dnd":remove_pixbuf, "offline":remove_pixbuf}
self.tree.set_model(self.treestore)
self.queueOUT = queueOUT
self.optionmenu = self.xml.get_widget('optionmenu')
self.optionmenu.set_history(6)
self.tab_messages = {}
#colonnes
self.col = gtk.TreeViewColumn()
render_pixbuf = gtk.CellRendererPixbuf()
self.col.pack_start(render_pixbuf, expand = False)
self.col.add_attribute(render_pixbuf, 'pixbuf', 0)
render_text = gtk.CellRendererText()
self.col.pack_start(render_text, expand = True)
self.col.add_attribute(render_text, 'text', 1)
self.tree.append_column(self.col)
#signales
self.xml.signal_connect('gtk_main_quit', self.on_quit)
self.xml.signal_connect('on_quit_activate', self.on_quit)
self.xml.signal_connect('on_treeview_event', self.on_treeview_event)
self.xml.signal_connect('on_status_changed', self.on_status_changed)
self.xml.signal_connect('on_row_activated', self.on_row_activated)
# self.mk_menu_c()
class plugin:
def read_queue(self):
while self.queueIN.empty() == 0:
ev = self.queueIN.get()
print ev
if ev[0] == 'ROSTER':
self.r.mkroster2(ev[1])
elif ev[0] == 'NOTIFY':
self.r.chg_status(ev[1][0], ev[1][1], ev[1][2])
elif ev[0] == 'MSG':
if not self.r.tab_messages.has_key(ev[1][0]):
self.r.tab_messages[ev[1][0]] = message(ev[1][0], self.r)
self.r.tab_messages[ev[1][0]].print_conversation(ev[1][1])
return 1
def __init__(self, quIN, quOUT):
gtk.threads_init()
gtk.threads_enter()
self.queueIN = quIN
self.r = roster(quOUT)
self.time = gtk.timeout_add(200, self.read_queue)
gtk.main()
gtk.threads_leave()
if __name__ == "__main__":
plugin(None, None)
print "plugin gui loaded"
#!/usr/bin/env python
## runCore.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@crans.org>
## - Vincent Hanquez <tab@tuxfamily.org>
## - David Ferlier <krp@yazzy.org>
##
## Copyright (C) 2003 Gajim Team
##
## 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 logging
logging.basicConfig()
import sys
sys.path.append("..")
import common
import core
core.core.start()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment