using System;
using System.IO;
using System.Collections;
using System.Timers;
using System.Threading;
using System.Reflection;
using System.Resources;
using Gtk;
//using Gdk;
using System.Diagnostics;
using Lumpio.Net.Msn;
//using Lumpio.Ink;
namespace Lumpio.LuMsn {
public class LuMsn {
MsnConversation convo = null;
public MsnClient msn = null;
Window connecting;
VBox connVBox;
Label connLabel;
Button cancButton;
Window contactList;
VBox contVBox;
ScrolledWindow sw;
TreeView tree;
Button imButton;
TreeStore contactStore;
ArrayList users;
public ArrayList convos;
public Gdk.Pixbuf iconUserTyping;
public Gdk.Pixbuf[] presenceIcons;
System.Timers.Timer msnTimer;
ThreadNotify msnTimerNotify;
public static void Main(string[] args) {
//Gtk.Application.Init();
new LuMsn(args);
}
public LuMsn(string[] args) {
Gtk.Application.Init();
Assembly asm = Assembly.GetExecutingAssembly();
iconUserTyping = new Gdk.Pixbuf(asm, "UserTypingIcon");
Gdk.Pixbuf presenceIconStrip = new Gdk.Pixbuf(asm, "PresenceIcons");
presenceIcons = getImageStrip(presenceIconStrip, 16);
users = new ArrayList();
convos = new ArrayList();
Debug.Listeners.Add(
new TextWriterTraceListener(System.Console.Out));
createGui();
connecting.SetPosition(WindowPosition.Center);
connecting.ShowAll();
msn = new MsnClient();
//msn.ClientCapabilities |= MsnClientCapabilities.ReceiveInk | MsnClientCapabilities.ReceiveSendInk;
msn.Connected += new MsnConnectEventHandler(connected);
msn.Invited += new MsnInviteEventHandler(invite);
msn.PresenceChanged += new MsnPresenceEventHandler(presence);
Debug.WriteLine("Connecting...");
msnTimerNotify = new ThreadNotify(updateMsn);
msnTimer = new System.Timers.Timer(100);
msnTimer.Elapsed += new ElapsedEventHandler(msnTimerElapsed);
msnTimer.AutoReset = true;
msnTimer.Start();
// Put passport and password HERE:
msn.Passport = "passport";
msn.Password = "password";
msn.Connect();
/*while (go) {
Gtk.Application.RunIteration(false);
msn.Run();
Thread.Sleep(10);
}*/
Gtk.Application.Run();
while (convos.Count > 0) {
((Conversation)convos[0]).Close();
}
msnTimer.Stop();
msn.Signoff();
msn.Run();
}
private void msnTimerElapsed(object sender, ElapsedEventArgs args) {
msnTimerNotify.WakeupMain();
}
private void updateMsn() {
msn.Run();
}
private void userQuit() {
Debug.WriteLine("User quit");
Gtk.Application.Quit();
}
private void windowDelete(object sender, DeleteEventArgs args) {
userQuit();
}
private void cancelClicked(object sender, EventArgs args) {
userQuit();
}
private void startConvo(TreeIter iter) {
foreach (User u in users) {
if (u.ti.Equals(iter)) {
foreach (Conversation co in convos) {
if (!co.isChat && co.firstUser == u.user) {
co.PresentWindow();
return;
}
}
Debug.WriteLine("Starting conversation with " + u.user.Passport);
MsnConversation convo = msn.StartConversation();
//convo.Invite(u.user);
Conversation c = new Conversation(u.user, convo, this);
convos.Add(c);
}
}
}
private void startConvo(object sender, RowActivatedArgs args) {
//Debug.WriteLine("Row activated " + args.Path.ToString());
TreeIter iter;
if (contactStore.GetIter(out iter, args.Path)) {
startConvo(iter);
}
}
private void startConvo(object sender, EventArgs args) {
TreeIter iter;
TreeModel model;
if (tree.Selection.GetSelected(out model, out iter)) {
startConvo(iter);
}
}
private void createGui() {
connecting = new Window("LSMN - Connecting...");
connecting.BorderWidth = 20;
connVBox = new VBox();
connVBox.Spacing = 20;
connLabel = new Label("Connecting to Msn, please wait");
connVBox.PackStart(connLabel);
cancButton = new Button(Stock.Cancel);
cancButton.Clicked += new EventHandler(cancelClicked);
connVBox.PackStart(cancButton);
connecting.Add(connVBox);
connecting.DeleteEvent += new DeleteEventHandler(windowDelete);
contactList = new Window("LMSN - Contact list");
//contactList.BorderWidth = 3;
contactList.SetDefaultSize(300, 600);
contVBox = new VBox();
contVBox.Spacing = 3;
contVBox.Homogeneous = false;
contactStore = new TreeStore(typeof(Gdk.Pixbuf), typeof(string));
sw = new ScrolledWindow();
tree = new TreeView();
tree.Model = contactStore;
tree.HeadersVisible = false;
tree.RowActivated += new RowActivatedHandler(startConvo);
TreeViewColumn col = tree.AppendColumn("Status", new CellRendererPixbuf(), "pixbuf", 0);
col.Spacing = 0;
tree.AppendColumn("Nickname", new CellRendererText(), "markup", 1);
contactStore.SetSortFunc(0, new TreeIterCompareFunc(sortCompareFunc), (IntPtr)0, null);
contactStore.SetSortColumnId(0, SortType.Ascending);
sw.Add(tree);
sw.Show();
contVBox.PackStart(sw, true, true, 0);
imButton = new Button("IM");
imButton.HeightRequest = 25;
imButton.Clicked += new EventHandler(startConvo);
contVBox.PackStart(imButton, false, false, 0);
contactList.Add(contVBox);
contactList.DeleteEvent += new DeleteEventHandler(windowDelete);
}
private int sortCompareFunc(TreeModel model, TreeIter i1, TreeIter i2) {
int sv1 = 0, sv2 = 0;
foreach (User u in users) {
if (u.ti.Equals(i1)) {
sv1 = sortValueFor(u.user.Presence);
}
if (u.ti.Equals(i2)) {
sv2 = sortValueFor(u.user.Presence);
}
}
return Math.Sign(sv2 - sv1);
}
private int sortValueFor(MsnPresence presence) {
switch (presence) {
case MsnPresence.Offline:
return 7;
case MsnPresence.Available:
return 8;
case MsnPresence.Idle:
return 1;
case MsnPresence.BeRightBack:
return 2;
case MsnPresence.Away:
return 3;
case MsnPresence.OnThePhone:
return 4;
case MsnPresence.OutToLunch:
return 5;
case MsnPresence.Busy:
return 6;
}
Debug.WriteLine("This isn't supposed to happen!!!!!!!!!!!!!!!");
return 0;
}
private void connected(object sender, EventArgs args) {
if (sender == msn) {
Console.WriteLine("Connected");
foreach (MsnUser user in msn.ContactList) {
//User u = new User(user, contactStore.AppendValues(presenceIcons[(int)user.Presence], user.NickName + " <" + user.Passport + ">"));
User u = new User(user, contactStore.AppendValues(presenceIcons[(int)user.Presence], user.NickName + " " + user.PersonalMessage + ""));
users.Add(u);
}
connecting.HideAll();
contactList.ShowAll();
} else if (sender == convo) {
convo.Invite(msn.ContactList.GetByPassport("lumpio2@hotmail.com"));
} else if (sender is MsnConversation) {
Debug.WriteLine("Conversation has been synced");
}
}
/*private string statusString(MsnPresence p) {
switch (p) {
case MsnPresence.Offline:
return "-";
case MsnPresence.Available:
return "+";
case MsnPresence.Busy:
return "+BSY";
case MsnPresence.Idle:
return "+...";
case MsnPresence.BeRightBack:
return "+BRB";
case MsnPresence.Away:
return "+AWA";
case MsnPresence.OnThePhone:
return "+PHN";
case MsnPresence.OutToLunch:
return "+LUN";
}
return "";
}*/
private void invite(object sender, MsnInviteEventArgs args) {
Conversation nc;
Debug.WriteLine(args.Inviter.NickName + " invites you to a conversation");
foreach (Conversation c in convos) {
if (!c.isChat && c.firstUser == args.Inviter) {
c.Convo = args.Conversation;
return;
}
}
nc = new Conversation(null, args.Conversation, this);
convos.Add(nc);
}
private void presence(object sender, MsnPresenceEventArgs args) {
foreach (User u in users) {
if (u.user == args.User) {
//contactStore.SetValue(u.ti, 0, statusString(args.User.Presence));
contactStore.SetValue(u.ti, 0, presenceIcons[(int)args.User.Presence]);
contactStore.SetValue(u.ti, 1, args.User.NickName + " " + args.User.PersonalMessage + "");
}
}
}
private Gdk.Pixbuf[] getImageStrip(Gdk.Pixbuf buf, int width) {
int numframes = buf.Width / width;
Gdk.Pixbuf[] bufs = new Gdk.Pixbuf[numframes];
for (int i = 0; i < numframes; ++i) {
bufs[i] = new Gdk.Pixbuf(buf, width * i, 0, width, buf.Height);
}
return bufs;
}
class Conversation {
LuMsn msnc;
public MsnUser firstUser = null;
//public MsnUserCollection users;
public Window win = null;
Image typing;
Notebook tabs;
ScrolledWindow sw;
TextView tv;
TextView msgtv;
// InkCollector inkcoll;
Button sendbtn;
MenuBar menubar;
Queue msgQueue;
Queue lastTrIDs;
public bool isChat = false;
TextTag ownmsgtag, othermsgtag, infomsgtag, useraddtag, errortag;
System.Timers.Timer typingTimer;
ThreadNotify typingTimerNotify;
System.Timers.Timer selfTypingTimer;
ThreadNotify selfTypingTimerNotify;
MsnConversation convo = null;
public MsnConversation Convo {
get {
return convo;
}
set {
if (convo != null && convo.state != MsnConversation.ConnectionState.Closed) {
convo.Close();
}
convo = value;
convo.UserJoined += new MsnConversation.JoinEventHandler(join);
convo.UserLeft += new MsnConversation.LeaveEventHandler(leave);
convo.MessageReceived += new MsnConversation.MessageEventHandler(message);
convo.Connected += new MsnConnectEventHandler(connect);
convo.MessageConfirmed += new MsnConversation.MessageConfirmationEventHandler(messageConfirm);
}
}
bool windowOpened = false;
public Conversation(MsnUser invite, MsnConversation convo, LuMsn msnc) {
Convo = convo;
this.msnc = msnc;
this.firstUser = invite;
//users = new MsnUserCollection();
msgQueue = new Queue();
lastTrIDs = new Queue();
typingTimerNotify = new ThreadNotify(new ReadyEvent(hideTyping));
typingTimer = new System.Timers.Timer(10000);
typingTimer.Elapsed += new ElapsedEventHandler(hideTypingEvent);
selfTypingTimerNotify = new ThreadNotify(new ReadyEvent(selfTyping));
selfTypingTimer = new System.Timers.Timer(5000);
selfTypingTimer.AutoReset = true;
selfTypingTimer.Elapsed += new ElapsedEventHandler(selfTypingEvent);
}
public void PresentWindow() {
if (win != null) {
win.Present();
}
}
private void createWindow(string nick, string passport) {
VBox vbox;
HBox hbox;
VPaned vpaned;
Menu menu;
MenuItem pmi, mi;
win = new Window("Conversation: " + nick + " <" + passport + ">");
win.SetDefaultSize(500, 500);
win.DeleteEvent += new DeleteEventHandler(close);
vbox = new VBox();
vbox.Spacing = 3;
hbox = new HBox();
hbox.Spacing = 3;
menubar = new MenuBar();
menu = new Menu();
pmi = new MenuItem("Conversation");
//pmi.Submenu = mi;
mi = new MenuItem("Close");
mi.Activated += new EventHandler(close);
pmi.Submenu = menu;
menu.Append(mi);
menubar.Append(pmi);
hbox.PackStart(menubar);
typing = new Image(msnc.iconUserTyping);
hbox.PackStart(typing, false, false, 0);
vbox.PackStart(hbox, false, false, 0);
vpaned = new VPaned();
sw = new ScrolledWindow();
tv = new TextView();
tv.Editable = false;
tv.WrapMode = WrapMode.Word;
tv.CursorVisible = false;
ownmsgtag = new TextTag(null);
tv.Buffer.TagTable.Add(ownmsgtag);
ownmsgtag.ForegroundGdk = new Gdk.Color(0x00, 0x00, 0xDD);
ownmsgtag.Weight = Pango.Weight.Bold;
othermsgtag = new TextTag(null);
tv.Buffer.TagTable.Add(othermsgtag);
othermsgtag.ForegroundGdk = new Gdk.Color(0xDD, 0x00, 0x00);
othermsgtag.Weight = Pango.Weight.Bold;
infomsgtag = new TextTag(null);
tv.Buffer.TagTable.Add(infomsgtag);
infomsgtag.ForegroundGdk = new Gdk.Color(0x80, 0x80, 0x80);
infomsgtag.Style = Pango.Style.Italic;
useraddtag = new TextTag(null);
tv.Buffer.TagTable.Add(useraddtag);
useraddtag.ForegroundGdk = new Gdk.Color(0x00, 0xA0, 0xA0);
useraddtag.Style = Pango.Style.Italic;
errortag = new TextTag(null);
tv.Buffer.TagTable.Add(errortag);
errortag.ForegroundGdk = new Gdk.Color(0xA0, 0x00, 0x00);
errortag.Weight = Pango.Weight.Bold;
sw.Add(tv);
vpaned.Pack1(sw, true, true);
hbox = new HBox();
hbox.Spacing = 3;
tabs = new Notebook();
tabs.TabPos = PositionType.Bottom;
sw = new ScrolledWindow();
msgtv = new TextView();
msgtv.WrapMode = WrapMode.Word;
msgtv.KeyReleaseEvent += new KeyReleaseEventHandler(msgtvKeyUp);
msgtv.Buffer.Changed += new EventHandler(textChanged);
sw.Add(msgtv);
tabs.AppendPage(sw, new Label("Text"));
// inkcoll = new InkCollector();
// tabs.AppendPage(inkcoll, new Label("Ink"));
hbox.PackStart(tabs);
sendbtn = new Button("Send");
sendbtn.Clicked += new EventHandler(send);
hbox.PackStart(sendbtn, false, false, 0);
vpaned.Pack2(hbox, true, true);
vbox.PackStart(vpaned);
win.Add(vbox);
win.ShowAll();
win.FocusChild = msgtv;
typing.Hide();
}
private void msgtvKeyUp(object sender, KeyReleaseEventArgs args) {
if (args.Event.Key == Gdk.Key.Return) {
if ((args.Event.State & Gdk.ModifierType.ControlMask) == 0) {
send(null, null);
}
}
}
private void textChanged(object sender, EventArgs args) {
if (!selfTypingTimer.Enabled) {
if (msgtv.Buffer.Text != "") {
convo.TypingNotifyMessage();
selfTypingTimer.Stop();
selfTypingTimer.Start();
} else if (msgtv.Buffer.Text == "") {
selfTypingTimer.Stop();
}
}
}
private void send(object sender, EventArgs args) {
if (tabs.CurrentPage == 0) {
string text = msgtv.Buffer.Text.TrimEnd(new char[]{'\r', '\n'});
msgtv.Buffer.Text = "";
if (text == "/debug") {
Debug.WriteLine("CONVOS ON LIST: " + msnc.convos.Count);
Debug.WriteLine("USERS ON LIST: " + convo.Users.Count);
return;
} else if (text == "/nudge") {
addInfoMsg("Sending a nudge");
convo.NudgeMessage();
return;
} else if (text.IndexOf("/ink") == 0) {
if (text.Length < 5) {
return;
}
string fn = text.Substring(5);
FileStream fs;
try {
fs = File.OpenRead(fn);
} catch (Exception e) {
addText("File couldn't be opened: " + e.Message + "\r\n", errortag);
return;
}
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
convo.GifMessage(data);
addText(msnc.msn.NickName + ":\r\n", ownmsgtag);
addPixbuf(new Gdk.Pixbuf(new MemoryStream(data)));
addText("\r\n", null);
return;
}
addText(msnc.msn.NickName + ": ", ownmsgtag);
addText(text + "\r\n", null);
if (convo.Users.Count == 0) {
msgQueue.Enqueue(text);
convo.Invite(firstUser);
} else {
sendMessage(text);
}
}/* else {
int w, h;
Gdk.Pixmap map = inkcoll.Ink.Render(win.GdkWindow.Depth, out w, out h);
Gdk.Pixbuf buf = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, w, h);
buf = buf.GetFromDrawable(map, win.GdkWindow.Colormap, 0, 0, 0, 0, w, h);
buf.Savev("temp_ink.gif", "gif", null, null);
FileStream fs;
try {
fs = File.OpenRead("temp_ink.gif");
} catch (Exception e) {
addText("File couldn't be opened: " + e.Message + "\r\n", errortag);
return;
}
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
convo.GifMessage(data);
addText(msnc.msn.NickName + ":\r\n", ownmsgtag);
addPixbuf(buf);
addText("\r\n", null);
inkcoll.Ink.Clear();
}*/
}
private void sendMessage(string text) {
selfTypingTimer.Stop();
UInt32 trid = convo.PlainTextMessage(text);
lastTrIDs.Enqueue(trid);
if (lastTrIDs.Count > 10) {
lastTrIDs.Dequeue();
}
}
private void addText(string text, TextTag tag) {
TextIter iter = tv.Buffer.EndIter;
if (tag == null) {
tv.Buffer.Insert(ref iter, text);
} else {
tv.Buffer.InsertWithTags(ref iter, text, new TextTag[]{tag});
}
TextMark mark = tv.Buffer.CreateMark(null, tv.Buffer.EndIter, true);
tv.ScrollMarkOnscreen(mark);
tv.Buffer.DeleteMark(mark);
}
private void addPixbuf(Gdk.Pixbuf buf) {
TextIter iter = tv.Buffer.EndIter;
tv.Buffer.InsertPixbuf(ref iter, buf);
TextMark mark = tv.Buffer.CreateMark(null, tv.Buffer.EndIter, true);
tv.ScrollMarkOnscreen(mark);
tv.Buffer.DeleteMark(mark);
}
private void addInfoMsg(string msg) {
addText(msg + "\r\n", infomsgtag);
}
private void connect(object sender, EventArgs args) {
if (convo.Users.Count == 0 && firstUser != null) {
Debug.WriteLine("Inviting " + firstUser.NickName);
convo.Invite(firstUser);
}/* else {
Debug.WriteLine("Connected, opening window");
createWindow(convo.Users[0].NickName);
}*/
}
private void join(object sender, MsnConversation.JoinEventArgs args) {
if (firstUser == null) {
firstUser = args.User;
}
if (windowOpened == false) {
createWindow(args.User.NickName, args.User.Passport);
windowOpened = true;
}
if (args.User != firstUser) {
addText(args.User.ToString() + " has been added to conversation\r\n", useraddtag);
}
if (convo.Users.Count > 1) {
isChat = true;
win.Title = "Chat: " + convo.Users.Count + " users";
}
//if (!isChat) {
while (msgQueue.Count > 0) {
sendMessage((string)msgQueue.Dequeue());
}
//}
//users.Add(args.User);
}
private void leave(object sender, MsnConversation.LeaveEventArgs args) {
//users.Remove(args.User);
if (args.Timeout) {
addInfoMsg("The conversation timed out");
} else {
addInfoMsg(args.User + " has left the conversation");
}
if (isChat) {
win.Title = "Chat: " + convo.Users.Count + " users";
}
}
private void message(object sender, MsnConversation.MessageEventArgs args) {
if (!windowOpened) {
Debug.WriteLine("--- OPENING WINDOW ---");
createWindow(convo.Users[0].NickName, convo.Users[0].Passport);
}
if (args.Message.MessageType == MsnMessageType.PlainText) {
addText(args.Message.From.NickName + ": ", othermsgtag);
addText(args.Message.Body + "\r\n", null);
hideTyping();
} else if (args.Message.MessageType == MsnMessageType.TypingNotify) {
//typing.Text = args.Message.From.NickName + " is typing...";
typing.Show();
typingTimer.Stop();
typingTimer.Start();
} else if (args.Message.MessageType == MsnMessageType.Gif) {
Debug.WriteLine("GIF MESSAGE RECEIVED");
byte[] data = args.Message.DecodeBody();
if (data != null) {
addText(args.Message.From.NickName + ": \r\n", othermsgtag);
addPixbuf(new Gdk.Pixbuf(new MemoryStream(data)));
addText("\r\n", null);
}
}
}
private void messageConfirm(object sender, MsnConversation.MessageConfirmationEventArgs args) {
if (!args.Success) {
if (lastTrIDs.Contains(args.TrID)) {
addText("Message could not be delivered to all recipients\r\n", errortag);
}
}
}
private void selfTypingEvent(object sender, ElapsedEventArgs args) {
selfTypingTimerNotify.WakeupMain();
}
private void selfTyping() {
convo.TypingNotifyMessage();
}
private void hideTypingEvent(object sender, ElapsedEventArgs args) {
typingTimerNotify.WakeupMain();
}
private void hideTyping() {
typing.Hide();
//typing.Text = "";
}
public void Close() {
Debug.WriteLine("closing conversation");
selfTypingTimer.Stop();
typingTimer.Stop();
convo.Close();
win.Destroy();
msnc.convos.Remove(this);
}
private void close(object sender, EventArgs args) {
Close();
}
}
class User {
public TreeIter ti;
public MsnUser user = null;
public User(MsnUser user, TreeIter ti) {
this.user = user;
this.ti = ti;
}
}
}
}