// ex:sw=3 // ^-- shift-width 3 for vi //import java.awt.event.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; //import java.awt.*; import java.awt.Component; //import java.io.*; import java.io.IOException; //import java.util.*; import java.util.Vector; import java.util.Enumeration; import java.util.Hashtable; //import java.net.*; //import javax.swing.*; import javax.swing.DefaultListModel; import javax.swing.JPopupMenu; import javax.swing.JOptionPane; import javax.swing.JMenuItem; //import javax.swing.text.*; //import javax.swing.event.*; import lava.net.common.Value; import lava.net.common.VariableModifier; import lava.net.common.UNL; import lava.net.psyc.PSYCDeliveryException; import lava.net.psyc.PSYCMessageCenter; import lava.net.psyc.PSYCPacketManager; import lava.net.psyc.PSYCPacket; import lava.net.psyc.PSYCException; import lava.net.psyc.UNI; import lava.net.psyc.packages.Authentication; import lava.net.psyc.packages.AuthenticationListener; import lava.net.psyc.packages.AuthenticationWontGivePasswordException; import lava.net.psyc.packages.Biff; import lava.net.psyc.packages.BiffListener; import lava.net.psyc.packages.Conferencing; import lava.net.psyc.packages.ConferencingListener; import lava.net.psyc.packages.Echo; import lava.net.psyc.packages.Trace; import lava.net.psyc.packages.LinkPeer; import lava.net.psyc.packages.LinkPeerListener; //port lava.net.psyc.packages.Notification; //port lava.net.psyc.packages.NotificationListener; import lava.net.psyc.packages.Friends; import lava.net.psyc.packages.FriendsListener; import chat.*; import chat.Properties; import chat.TextListener; /** * Psychedelic: the primary PSYC chat & user presence application **/ public class Psychedelic implements ConferencingListener, // FriendsListener, // TextListener, // BuddyListener, UserServer, UserFrameListener, // PSYCPacketManager { public static String appTitle = // "Psychedelic 0.2 by symLynX using Bitkoenig's excellent PSYC Library."; public static String copyrightInfo = // "Copyright 1998, 1999 by LAVA, an iXL company."; public static String extraInfo = // "This is free software. See http://www.psyced.org for more information."; /** * **/ private static String[] args = null; /** * **/ private PSYCMessageCenter center = new PSYCMessageCenter(this); /** * **/ private Authentication authentication = null; /** * **/ private Biff biff = null; /** * **/ private Conferencing conferencing = null; /** * **/ private LinkPeer link = null; /** * **/ private Friends friends = null; /** * **/ private String me = null; /** * **/ private String base = null; /** * **/ private Vector places = new Vector(); /** * **/ private DefaultListModel buddies = new DefaultListModel(); /** * **/ private ConferencingWindow main = // new ConferencingWindow(this,this,null,Properties.statusTitle,false); /** * **/ private BuddyFrame buddy = new BuddyFrame(this,this,buddies); /** * **/ // mapping context|UNI -> ConferencingWindow private Hashtable windows = new Hashtable(); /** * **/ // mapping context -> UNL private Hashtable contextOwners = new Hashtable(); /** * **/ // mapping context -> Hashtable(UNI -> nick) private Hashtable nicks = new Hashtable(); /** * **/ public Psychedelic() { this (null); } /** * **/ public Psychedelic(String base) { super(); WindowListener exit = new WindowAdapter() { /** * **/ public void windowClosing(WindowEvent we) { try { link.unlink(); } catch(PSYCDeliveryException e) { } System.exit(0); } }; main.addWindowListener(exit); buddy.addWindowListener(exit); places.addElement(new UNL("psyc://beta.psyced.org/@Berlin")); places.addElement(new UNL("psyc://psyced.org/@PSYC")); places.addElement(new UNL("psyc://localhost/@RendezVous")); this.base = base; center.addPackage(new Echo()); center.addPackage(new Trace()); initAuthenticationPackage(); initLinkPackage(); center.addPackage(friends = new Friends(this)); center.addPackage(conferencing = new Conferencing(this)); center.setContentType(null,Properties.defaultContentType); initIdentification(args.length > 0 ? args[0] : null); main.showText(appTitle+"\n"+copyrightInfo+"\n"+extraInfo+"\n"); } /** * **/ private ConferencingWindow getWindow(Object identifier) { ConferencingWindow window = (ConferencingWindow)windows.get(identifier); if(window == null) { String name = null; if(identifier instanceof String) { // a Place UNL placeOwner = (UNL)contextOwners.get(identifier); if(placeOwner != null) name = getNick(placeOwner); if(name == null) name = placeOwner.toString(); window = new ConferencingWindow(this,this,(String)identifier,// Properties.placeTitlePrefix + name,true); } else { // a User UNL user = (UNL)identifier; if(user != null) name = getNick(user); window = new ConferencingWindow(this,this,null,// Properties.userTitlePrefix + name,false); } window.identifier = identifier; windows.put(identifier,window); window.showStatus(name); } return window; } /** * **/ private String getNick(String context, UNI user) { UNL lastSender = conferencing.getLastSender(context,user); if(lastSender == null) lastSender = user; String nick = getNick(lastSender); Hashtable nicks = (Hashtable)this.nicks.get(context); if(nicks == null) this.nicks.put(context,nicks = new Hashtable()); String oldNick = (String)nicks.get(user); if(oldNick == null) nicks.put(user,nick); else if(!oldNick.equals(nick)) { nicks.put(user,nick); nickChanged(context,user,oldNick,nick); } return nick; } /** * **/ private String getNick(UNL user) { String nick = center.getNickname(user); if(nick == null) nick = user.toString(); return nick; } /** * **/ public void nickChanged(String context, UNI user, String old, String n) { ConferencingWindow window = getWindow(context); window.showStatus("Nickchange: " + old + " -> " + n); window.members.setElementAt(new User(n,user),// window.members.indexOf(new User(old,user))); } /** * **/ public void contextIntroduced(UNL source, String context) { contextOwners.put(context,source); } /** * **/ public void placeEntered(String context, UNI user) { ConferencingWindow window = getWindow(context); String nick = getNick(context,user); window.showStatus(nick + " (" + user + ") entered place"); window.members.addElement(new User(nick,user)); } /** * **/ public void placeLeft(String context, UNI user) { ConferencingWindow window = getWindow(context); String nick = getNick(context,user); window.showStatus(nick + " (" + user + ") left place"); window.members.removeElement(new User(nick,user)); Hashtable nicks = (Hashtable)this.nicks.get(context); if(nicks == null) return; nicks.remove(user); } /** * **/ public void placeDeleted(String context) { contextOwners.remove(context); ConferencingWindow window = // (ConferencingWindow)windows.remove(context); if(window != null) window.dispose(); } /** * **/ public void memberChanged(String context, UNI user, UNL multicaster, // Hashtable locations, boolean speak, boolean listen, boolean authority) { ConferencingWindow window = getWindow(context); window.showStatus(getNick(context,user) + " (" + user + ") changed status"); } /** * **/ public void placePropertiesChanged(String context, // boolean allowExternals, // boolean speakersAddSpeakers, boolean speakersAddListeners, // boolean listenersAddListeners, // boolean speakersRemoveSpeakers, boolean speakersRemoveListeners) { ConferencingWindow window = getWindow(context); String name = null; UNL placeOwner = (UNL)contextOwners.get(context); if(placeOwner != null) name = getNick(placeOwner); if(name == null) name = placeOwner.toString(); window.showStatus(name + " changed properties"); } private void conversation(ConferencingWindow window, UNL user, String message, String open, String close) { String action = center.getAction(user); if (action == null) { if (message != null) window.showText(open + getNick(user) + close + message); } else if (message == null) window.showText(getNick(user) +" "+action+"."); else window.showText(getNick(user) +" "+action+": "+ message); } /** * **/ public void conversationEcho(String message) { // main.showStatus(Properties.i_say_prefix + message); } /** * **/ public void conversationPublic(String context, UNI user, String message) { ConferencingWindow window = getWindow(context); conversation(window, (UNL)user, message, Properties.public_open, Properties.public_close); // window.showText(Properties.public_open + getNick(context,user) + // // Properties.public_close + message); } /** * **/ public void conversationPager(UNL user, String message) { main.showText(Properties.pager_open + getNick(user) + // Properties.pager_close + message); } /** * **/ public void conversationPrivate(UNL user, String message) { ConferencingWindow window = getWindow(user); // in case the room sends stuff itself.. if (center.getActualMethod(user).startsWith("_message_public")) { conversation(window, user, message, Properties.public_open, Properties.public_close); } else conversation(window, user, message, Properties.private_open, Properties.private_close); } /** * **/ public void closedWindow(ConferencingWindow window) { if(window.identifier == null) return; if(window.identifier instanceof String) { // a Place UNL placeOwner = (UNL)contextOwners.get((String)window.identifier); if(placeOwner != null) try { link.unlink(placeOwner.withoutContext()); } catch(PSYCDeliveryException e) { } } } /** * **/ public void gotText(ConferencingWindow window, String text) { window.showStatus(""); if(text == null || text.length() <= 0) { /* was macht swing da falsch!? // clear on empty input - a chat-gui-rule by the lynx :) window.clear(); */ return; } /* else window.showStatus(""); */ if(text.charAt(0) == '/') { int i = text.indexOf(' '); if(i <= 0) i = text.length(); String command = null; try { command = text.substring(1,i); } catch(StringIndexOutOfBoundsException e) { command = null; } if(command == null) return; String args = null; try { args = text.substring(i + 1,text.length()); } catch(StringIndexOutOfBoundsException e) { args = null; } if(args != null && args.length() <= 0) args = null; if(command.equalsIgnoreCase("enter")) { if(args == null) { window.showStatus("Enter which place?"); return; } enter(new UNL(args)); return; } if(command.equalsIgnoreCase("connect")) { initIdentification(args); return; } if (window.identifier != null) { try { center.send( (UNL)window.identifier, "_request_execute", text ); } catch(PSYCDeliveryException e) { window.showStatus("Could not transmit command to destination."); } return; } } if(window.identifier == null) { try { window.showText(Properties.i_do_prefix + text); center.send( center.getIdentification(null), "_request_execute", text ); } catch(PSYCDeliveryException e) { window.showStatus("Could not transmit command to home server."); } return; } window.showText(Properties.i_say_prefix + text); if(window.identifier instanceof String) { conferencing.send((String)window.identifier,text); // a Place } else { conferencing.send((UNL)window.identifier,text); // a User } } /** * **/ private void enter(UNL server) { conferencing.acceptPlace(server); try { link.link(server); } catch(PSYCDeliveryException e) { // TODO } } /** * A friend in the people list has been selected. * Callback method from PeoplePanel. * added doubleclick support.. -lynx **/ public void friendSelected(String context, UNI user, int clickCount) { // if (clickCount > 1) { talk(context, user); return; /* all of the following stuff isnt working yet, so why bother the user with it? } if(context == null) { // a buddy UserFrame frame = new UserFrame(this,false,// null,user,// center.getNickname(user),// center.getName(user),// center.getHumanInformation(user),// center.getURLDescription(user)); } else { // a channel member UNL lastSender = conferencing.getLastSender(context,user); if(lastSender == null) lastSender = user; UserFrame frame = new UserFrame(this,false,// context,user,// getNick(context,user), center.getName(lastSender), center.getHumanInformation(lastSender), center.getURLDescription(lastSender)); } */ } /** * **/ public void showMe() { UNI user = center.getIdentification(null); UserFrame frame = new UserFrame(this,false,// null,user,// center.getNickname(user),// center.getName(user),// center.getHumanInformation(user),// center.getURLDescription(user)); /* */ } /** * **/ public void showPlaces(Component c, int x, int y) { Enumeration e = this.places.elements(); if(!e.hasMoreElements()) return; JPopupMenu places = new JPopupMenu("Places"); for(;e.hasMoreElements();) { UNL p = (UNL)e.nextElement(); JMenuItem i = new JMenuItem(p.toString()); if(link.isLinked(p)) i.setEnabled(false); i.addActionListener(new ActionListener() { /** * **/ public void actionPerformed(ActionEvent e) { enter(new UNL(e.getActionCommand())); } } ); places.add(i); } places.show(c,x,y); } /** * **/ public void talk(String context, UNI user) { UNL target = user; if(context != null) { UNL lastSender = conferencing.getLastSender(context,user); if(lastSender != null) target = lastSender; } target = target.withoutContext(); String name = getNick(target); ConferencingWindow window = new ConferencingWindow(this,this,null,// Properties.userTitlePrefix + name,false); window.identifier = target; windows.put(target,window); } /** * **/ public void friendPresent(UNI source, String nick) { if (nick == null) nick = source.getResource(); // main.showText(Properties.prefix + getNick(source) + " (" + source + // // ") signs on"); buddies.addElement(new User(nick, source)); } /** * **/ public void friendAbsent(UNI source, String nick) { if (nick == null) nick = source.getResource(); // main.showText(Properties.prefix + getNick(source) + " (" + source + // // ") signs off"); buddies.removeElement(new User(nick, source)); } /** * **/ private void initAuthenticationPackage() { center.addPackage(authentication = new Authentication(// new AuthenticationListener() { /** * **/ public String authenticationGetPassword(UNL server, String body) // throws AuthenticationWontGivePasswordException { String password = queryPassword(); if(password == null) throw new AuthenticationWontGivePasswordException(); return password; } /** * **/ public void authenticationInvalidPassword(UNL server, String body) { /* JOptionPane.showMessageDialog(main,// Properties.invalidPasswordMessage,// Properties.messageTitle,JOptionPane.ERROR_MESSAGE); */ } } )); } /** * **/ private void initLinkPackage() { center.addPackage(link = new LinkPeer(// new LinkPeerListener() { /** * **/ public void serverLinked(UNL server, String body) { main.showText(Properties.prefix + "Connected to " + server); // (body == null || body.length() <= 0 ? "" : " (" + body + ")")); if(server.equals(center.getIdentification(null))) { // main.identifier = server; center.setName(null,center.getName(server)); center.setDescription(null,center.getDescription(server)); center.setNickname(null,center.getNickname(server)); center.setURLDescription(null,// center.getURLDescription(server)); center.setHumanInformation(null,// center.getHumanInformation(server)); //center.setContentType(null, center.getContentType(server)); Value bud = center.getValue(server,"_friends"); if(bud != null) { buddies.clear(); String[] list = bud.toList(); for(int i = 0;i < list.length;++i) { UNI b = new UNI(server, list[i]); buddies.addElement(new User(b.getResource(),b)); } } } } /** * **/ public void serverUnlinked(UNL server, String body) { // main.identifier = null; main.showText(Properties.prefix + "Disconnected from " + server + // (body == null || body.length() <= 0 ? "" : " (" + body + ")")); main.showStatus(Properties.prefix + "Use /connect to reconnect"); } } )); } /** * **/ private void initIdentification(String arg) { try { link.unlink(); } catch(PSYCDeliveryException e) { } //center.setIdentification(null, arg == null ? // queryIdentification() : new UNI(arg) ); if (arg == null) { if (me == null) { main.showStatus("Use /connect to start."); return; } else center.setIdentification(null, new UNI(me) ); } else { me = arg; center.setIdentification(null, new UNI(arg) ); } try { link.link(); } catch(PSYCDeliveryException e) { center.setIdentification(null,null); /* try { JOptionPane.showMessageDialog(main,// Properties.userProgramNotReachableMessage,// Properties.messageTitle,// JOptionPane.ERROR_MESSAGE); catch(IOException e) { } } */ } } /** * apparently the 2nd shown JOptionPane crashes my jvm * i wonder if that only happens on DECalpha.. * anyway that's why i decided to use the password query only * which doesnt seem to produce problems * private UNI queryIdentification() throws IOException { String input = JOptionPane.showInputDialog(main,// Properties.userProgramQueryMessage,Properties.messageTitle,// JOptionPane.QUESTION_MESSAGE); if(input == null || input.length() <= 0) throw new IOException(); return new UNI(input); } **/ /** * this one works fine.. why? then again, i'm glad it does! **/ private String queryPassword() { return JOptionPane.showInputDialog(main,// Properties.passwordQueryMessage,Properties.messageTitle,// JOptionPane.QUESTION_MESSAGE); } /** * **/ final public static void main(String args[]) { Psychedelic.args = args; new Psychedelic(); } /** * **/ public void error(PSYCException e, UNL remote) { // System.err.println("error: "+e.toString()); main.showStatus(e.toString()+" (to "+remote.toString()+")"); } /** * **/ public void reset(UNL remote) { // System.err.println("reset: "+remote.toString()); main.showStatus("Lost link to "+remote.toString()+"."); } /** * **/ public void manage(PSYCPacket packet) { String mc = packet.getMethod(); if (mc == null) return; // why does this happen? UNL source = packet.getSource(); System.err.println("("+mc+") from "+source); String body = packet.getStringBody(); /* Vector header = packet.getHeader(); VariableModifier m; for(int i=0; i < header.size(); ++i) { m = (VariableModifier)header.elementAt(i); System.err.println(m.toString()); // viel zu simpel, erkennt keine _nick_alt oder _action_cool ! if (m.getName().equals("_nick")) nick = m.getValue().toString(); if (m.getName().equals("_action")) action = m.getValue().toString(); } */ /* // this message handler does it nicely, but in the wrong place :( String nick = center.getNickname(source); String action = center.getAction(source); if (body != null) { if (nick != null) { if (action != null) body = nick+" "+action+": "+body; else body = "("+nick+") "+body; } else { if (mc.startsWith("_message_echo")) { main.showText("] "+ body); return; } main.showStatus(body); return; } } else if (action != null) { if (nick != null) body = nick+" "+action+"."; else { String me = center.getNickname(null); body = (me != null ? me : "me") +" "+action+"."; } } if (body != null) main.showText(body); */ // hierarchies are useful, see? if (body == null || mc.startsWith("_message")) return; if (mc.startsWith("_list")) { while(body.length() > 0 && body.charAt(body.length()-1) == '\n') body = body.substring(0,body.length()-1); main.showText(body); } else main.showStatus(body); } }