/*
 Eteria IRC Client, an RFC 1459 compliant client program written in Java.
 Copyright (C) 2000-2001  Javier Kohen <jkohen at tough.com>
 
 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; either version 2 of the License, 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 General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 import java.awt.*;
 import java.awt.event.*;
 import java.io.IOException;
 import java.lang.reflect.*;
 import java.net.URL;
 import java.text.Collator;
 import java.text.MessageFormat;
 import java.util.Enumeration;
 import java.util.Observable;
 import java.util.StringTokenizer;
 import ar.com.jkohen.awt.*;
 import ar.com.jkohen.irc.*;
 import ar.com.jkohen.util.ConfigurationProperties;
 import ar.com.jkohen.util.Resources;
 
 class ChannelWindow extends ChatWindow implements ActionListener, MouseListener, KeyListener, ItemListener
 {
 	protected TextField topic;
 	protected TextFieldHistory entry;
 	protected NickList nick_list;
 	protected ImageButton close;
 	protected ImageButton kill;
 	protected PopupMenu popup_menu;
 	protected Menu child_menu;
 	protected Menu ircop_menu;
 	private Choice color;
 
 	/* Configuration properties. */
 	private boolean have_halfops;
 
 	private EIRC eirc;
 	private Channel channel;
 	private Resources res;
 
 	private String item_under_menu;
 	private boolean is_op;
 	private boolean is_hop;
 	private boolean is_adm;
 	private boolean is_own;
 
 	/* Nick Completion. */
 	private String complete_partial;
 	private String complete_current = "";
 
 	public ChannelWindow(EIRC eirc, Channel channel)
 	{
 		super(channel.getTag());
 
   		this.eirc = eirc;
 		this.channel = channel;
 
 		text_canvas.setMode(eirc.scrollSpeed());
 
 		GridBagLayout gb = new GridBagLayout();
 		GridBagConstraints gbc = new GridBagConstraints();
 
 		setLayout(gb);
 		gbc.insets = new Insets(2, 2, 2, 2);
 		gbc.gridy = 0;
 
 		/* Component's initialization
 		 */
 
 		topic = new TextFieldHistory(eirc.getFrame());
 		gbc.weightx = 1.0;
 //		gbc.gridwidth = GridBagConstraints.REMAINDER;

 		gbc.gridwidth = 3;
 		gbc.fill = GridBagConstraints.HORIZONTAL;
 		gb.setConstraints(topic, gbc);
 		add(topic);
 
 		/* Next Row */
 		gbc.gridy++;
 
 		gbc.weightx = 1.0;
 		gbc.weighty = 1.0;
 		gbc.gridwidth = 1;
 		gbc.fill = GridBagConstraints.BOTH;
 		gb.setConstraints(text_canvas, gbc);
 		add(text_canvas);
 
 		gbc.weightx = 0.0;
 
 		/* Right Panel begins here. */
 
 		nick_list = new NickList();
 		gbc.weightx = 0;
 		gbc.gridwidth = 2;
 		gb.setConstraints(nick_list, gbc);
 		add(nick_list);
 
 		/* Right Panel ends here. */
 
 		// From here below, everything fills the space horizontally.

 		gbc.gridwidth = 1;
 	  	gbc.fill = GridBagConstraints.NONE;
 		gbc.weightx = 1.0;
 		gbc.weighty = 0.0;
 
 		/* Next Row */
 		gbc.gridx = 0;
 		gbc.gridy++;
 
 		color = new Choice();
 		StringTokenizer tk = new StringTokenizer(res.getString("conf.write_col_list"), ",");
 		while (tk.hasMoreTokens())
 			color.add(tk.nextToken());
 		gbc.anchor = GridBagConstraints.WEST;
 		gb.setConstraints(color, gbc);
 		add(color);
 
 	 	gbc.weightx = 0;
 		gbc.gridx++;
 
 		kill = new ImageButton(Resources.getLabel("chan.text.kill"), Resources.getImage("chan.icon.kill"));
 		kill.setEnabled(true);
 		gb.setConstraints(kill, gbc);
 		add(kill);
 
 		gbc.gridx++;
 
 		close = new ImageButton(Resources.getLabel("chan.text.close"), Resources.getImage("chan.icon.close"));
 		gbc.anchor = GridBagConstraints.EAST;
 		close.setEnabled(true);
 		gb.setConstraints(close, gbc);
 		add(close);
 
 		gbc.gridx = 0;
 		gbc.gridy++;
 
 		this.entry = new TextFieldHistory(eirc.getFrame());
 		entry.setFocusTraversable(false);
 		setFocusTraversalKeysEnabled(entry, false);
 		gbc.anchor = GridBagConstraints.CENTER;
 		gbc.fill = GridBagConstraints.HORIZONTAL;
 		gbc.gridwidth = GridBagConstraints.REMAINDER;
 		gb.setConstraints(entry, gbc);
 		add(entry);
 
 		popup_menu = new PopupMenu();
 		child_menu = new Menu();
 		ircop_menu = new Menu();
 		// We delay the building of the menu up to the moment when our nick is added to the list. This is needed in order to know whether we are opped or not.

 		add(popup_menu);
 
 
 		/* Event Listeners
 		 */
 
 		channel.addObserver(this);
 		topic.addActionListener(this);
 		popup_menu.addActionListener(this);
 		nick_list.addActionListener(this);
 		nick_list.addMouseListener(this);
 		entry.addActionListener(this);
 		entry.addKeyListener(this);
 		color.addItemListener(this);
 	  	close.addActionListener(this);
 	  	kill.addActionListener(this);
 	}
 
 	public void requestFocus()
 	{
 		entry.requestFocus();
 	}
 
 	public Channel getChannel()
 	{
 		return channel;
 	}
 
 	protected String getNick()
 	{
 		return eirc.getNick();
 	}
 
 	public void clearUsers()
  	{
  		channel.clean();
 	}
 
 	protected void visitURL(URL url)
 	{
 		eirc.visitURL(url);
 	}
 
 	protected void joinChannel(String name)
 	{
 		eirc.joinChannel(name);
 	}
 
 	protected void bell()
 	{
 		eirc.playSound(res.EVENT_MSG);
 	}
 	
 	protected void talkTo(String nick)
 	{
    		String text = entry.getText();
    		nick += talkto;
 
    		// Insert nick at caret position.

    		int pos = entry.getCaretPosition();
 
    		text = text.substring(0, pos).concat(nick).concat(text.substring(pos));
 
    		entry.setText(text);
    		entry.setCaretPosition(pos + nick.length());
    		entry.requestFocus();
 	}	
 
 	protected void copyText(String s)
 	{
 		eirc.cutPaste(s);
 	}
 
 	/*
 	 * Nick completion methods.
 	 */
 
 	private void resetCompleteNick()
 	{
 		this.complete_current = "";
 	}
 
 	private void completeNick()
 	{
 		char [] text = entry.getText().toCharArray();
 		int pos = entry.getCaretPosition();
 		int begin = pos - 1;
 		int end = pos;
 
 		Collator collator = RFC1459.getCollator();
 
 		String [] list = nick_list.getNicks();
 		// We need them fed alphabetically for this algorithm to work.

 		for (int i = 0; i < list.length; i++)
 		{
 			for (int j = list.length - 1; j > i; j--)
 			{
 				if (collator.compare(list[i], list[j]) > 0)
 				{
 					String t = list[i];
 					list[i] = list[j];
 					list[j] = t;
 				}
 			}
 		}
 
 		// Look for nick's boundaries.

 		while (begin >= 0 && RFC1459.isDeclaredChar(text[begin]))
 			begin--;
 
 		while (end < text.length && RFC1459.isDeclaredChar(text[end]))
 			end++;
 
 		begin++;
 
 		// If we don't have a search in progress, store the new search string in complete_partial.

 	  	if (complete_current.length() == 0)
 	  	{
 			complete_partial = String.valueOf(text, begin, end - begin);
 	  	}
 
 		int partial_length = complete_partial.length();
 	  	int first_match = -1;
 	  	boolean found = false;
 
 		for (int i = 0; i < list.length; i++)
 		{
 			if (list[i].length() < partial_length)
 			{
 				continue;
 			}
 
 			// Collator doesn't provide a beginsWith method, so we need to do this instead.

 			String partial_nick = list[i].substring(0, partial_length);
 			if (collator.equals(partial_nick, complete_partial))
 			{
 				// We found a match.

 				if (first_match == -1)
 				{
   					// Save the first match from the list for the case that the end of the list is reached, and it's needed to re-cycle the list.

 					first_match = i;
 				}
 
   				int order = collator.compare(list[i], complete_current);
   				if (order > 0)
   				{
   					// This match comes after the one we had (even if current was empty, which means this is the first one).

   					complete_current = list[i];
 					found = true;
   					break;
   				}
 			}
 		}
 
 	  	if (first_match != -1 && !found)
 		{
   			// Re-cycle the list.

   			complete_current = list[first_match];
   			found = true;
 		}
 
 		if (found)
 		{
   			// Insert the completion.

   			String new_text = String.valueOf(text, 0, begin) . concat(complete_current) . concat(String.valueOf(text, end, text.length - end));
 	  		entry.setText(new_text);
 
 	  		// Set the caret position to the end of the completed nick.

   			entry.setCaretPosition(begin + complete_current.length());
   		}
 	}
 
 
 	private void update_channel(Object hints)
 	{
 		String action = hints.toString();
 
 		if (action == null || action.equals("topic"))
 		{
 			topic.setText(channel.getTopic());
 		}
 		
 		if (action == null || action.startsWith("add "))
 		{
 			User u = channel.get(action.substring(action.indexOf(' ') + 1));
 			nick_list.add(u);
 		}
 		
 		if (action == null || action.startsWith("remove "))
 		{
 			String s = action.substring(action.indexOf(' ') + 1);
 			nick_list.remove(s);
 		}
 
 		if (action == null || action.startsWith("rename "))
 		{
 			String s = action.substring(action.indexOf(' ') + 1, action.lastIndexOf(' '));
 			User u = channel.get(action.substring(action.lastIndexOf(' ') + 1));
 			nick_list.update(s, u);
 		}
 
 		if (action == null || action.startsWith("update "))
 		{
 			String s = action.substring(action.indexOf(' ') + 1);
 			User u = channel.get(s);
 			nick_list.update(s, u);
 		}
 
 		if (action == null || action.equals("refresh"))
 		{
 			nick_list.repaint();
 		}
 		
 		if (action == null || action.equals("mode") || action.startsWith("update "))
 		{
 			User user = channel.get(eirc.getNick());
 			if (user != null)
 			{
 				// It's possible to receive other commands before NAMES on JOIN.

 				this.is_op = user.isOp();
 				this.is_hop = user.isHalfOp();
 				this.is_adm = user.isAdmin();
 				this.is_own = user.isOwner();
 			}
 
 			topic.setEditable(is_own || is_adm || is_op || is_hop || channel.isTopicSettable() || eirc.canOverride());
 
 			buildPopupMenu(true);
 		}
 	}
 
 	private void update_properties(ConfigurationProperties props, Object arg)
 	{
 		super.update(props, arg);
 
 		if (arg == null || arg.equals("nick_item_renderer"))
 		{
 			nick_list.setItemRenderer(props.getInt("nick_item_renderer"));
 			nick_list.loadList(channel.elements());
 		}
 
 		if (arg == null || arg.equals("nick_item_sort_method"))
 			nick_list.setSortMethod(props.getInt("nick_item_sort_method"));
 
 		if (arg == null || arg.equals("have_halfops"))
 			this.have_halfops = props.getBoolean("have_halfops");
 
 		if (arg == null || arg.equals("write_color"))
 			color.select(props.getInt("write_color"));
 	}
 
 	public void update(Observable o, Object arg)
 	{
 		if (o instanceof Channel)
 			update_channel(arg);
 		else
 			update_properties((ConfigurationProperties) o, arg);
 	}
 	
 	public void setEnabled(boolean b)
 	{
 		topic.setEnabled(b);
 		entry.setEnabled(b);
 		close.setEnabled(b);
 		kill.setEnabled(b);
 		buildPopupMenu(false);
 	}
 
 	public void setFont(Font f)
 	{
 		super.setFont(f);
 		topic.setFont(f);
 		entry.setFont(f);
 		nick_list.setFont(f);
 		nick_list.repaint();
 		buildPopupMenu(true);
 		close.setFont(f);
 		kill.setFont(f);
 	}
 
 	public void setBackground(Color c)
 	{
 		super.setBackground(c);
   		close.setBackground(c);
   		kill.setBackground(c);
 	}
 /*
 	public void setForeground(Color c)
 	{
 		super.setForeground(c);
 		nick_list.setForeground(c);
 	}
 */
 	public void setTextBackground(Color c)
 	{
 		text_canvas.setBackground(c);
 		entry.setBackground(c);
 		topic.setBackground(c);
   		nick_list.setTextBackground(c);
 	}
 
 	public void setTextForeground(Color c)
 	{
 		text_canvas.setForeground(c);
 		entry.setForeground(c);
 		topic.setForeground(c);
   		nick_list.setTextForeground(c);
 	}
 
 	public void setSelectedBackground(Color c)
 	{
 		text_canvas.setSelectedBackground(c);
   		nick_list.setSelectedBackground(c);
 	}
 
 	public void setSelectedForeground(Color c)
 	{
   		nick_list.setSelectedForeground(c);
 	}
 
 	private synchronized void buildPopupMenu(boolean b)
 	{
 		String tag = channel.getTag();
 
 		PopupMenu pm = popup_menu;
 		Menu cm = child_menu;
 		Menu im = ircop_menu;
 		MenuItem mi;
 
 		pm.removeAll();
 		cm.removeAll();
 		im.removeAll();
 
 		mi = new MenuItem(res.getString("nicklist.popup.query"));
 		mi.setActionCommand("QUERY {0}");
 		pm.add(mi);
 		pm.addSeparator();
 		
 		/* Custom commands added */
 		String n = res.getString("nicklist.popup.customs");
 		try
 		{
 			int max = Integer.parseInt(n);
 			for (int i = 1; i &lt;= max; i++)
 			{
 				String name = res.getString("nicklist.popup.custom." + i);
 				String cmd = res.getString("nicklist.popup.command." + i);
 
 				if (name != null && cmd != null)
 				{
 					mi = new MenuItem(name);
 					mi.setActionCommand(cmd);
 					pm.add(mi);
 				}
 			}
 			pm.addSeparator();
 		}
 		catch (NumberFormatException ex) { System.err.println("Wrong custom menu number"); }
 		
 		/**/
 		
 		mi = new MenuItem(res.getString("nicklist.popup.ignore"));
 		mi.setActionCommand("IGNORE {0}");
 		pm.add(mi);
 		mi = new MenuItem(res.getString("nicklist.popup.unignore"));
 		mi.setActionCommand("UNIGNORE {0}");
 		pm.add(mi);
 		pm.addSeparator();
 		mi = new MenuItem(res.getString("nicklist.popup.ghost"));
 		mi.setActionCommand("GHOST");
 		pm.add(mi);
 
 		if (is_op || is_adm || is_own || eirc.canOverride())
 		{
 			pm.addSeparator();
 			cm = new Menu(res.getString("nicklist.popup.operators"));
 			cm.addActionListener(this);
 			mi = new MenuItem(res.getString("nicklist.popup.kick"));
 			mi.setActionCommand("KICK");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.kban"));
 			mi.setActionCommand("KBAN");
 			cm.add(mi);
 
 			cm.addSeparator();
 			mi = new MenuItem(res.getString("nicklist.popup.ban"));
 			mi.setActionCommand("MODE {1} +b {0}");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.unban"));
 			mi.setActionCommand("MODE {1} -b {0}");
 			cm.add(mi);
 
 			cm.addSeparator();
 			mi = new MenuItem(res.getString("nicklist.popup.admin"));
 			mi.setActionCommand("MODE {1} +a {0}");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.except"));
 			mi.setActionCommand("MODE {1} +e {0}");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.invite"));
 			mi.setActionCommand("MODE {1} +I {0}");
 			cm.add(mi);
 
 			cm.addSeparator();
 			mi = new MenuItem(res.getString("nicklist.popup.voice"));
 			mi.setActionCommand("MODE {1} +v {0}");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.unvoice"));
 			mi.setActionCommand("MODE {1} -v {0}");
 			cm.add(mi);
 			
 			if (have_halfops)
 			{
 				cm.addSeparator();
 				mi = new MenuItem(res.getString("nicklist.popup.hop"));
 				mi.setActionCommand("MODE {1} +h {0}");
 				cm.add(mi);
 				mi = new MenuItem(res.getString("nicklist.popup.dehop"));
 				mi.setActionCommand("MODE {1} -h {0}");
 				cm.add(mi);
 			}
 
 			cm.addSeparator();
 			mi = new MenuItem(res.getString("nicklist.popup.op"));
 			mi.setActionCommand("MODE {1} +o {0}");
 			cm.add(mi);
 			mi = new MenuItem(res.getString("nicklist.popup.deop"));
 			mi.setActionCommand("MODE {1} -o {0}");
 			cm.add(mi);
 
 			pm.add(cm);
 		}
 		else
 		{
 			if (is_hop)
 			{
 				pm.addSeparator();
 				cm = new Menu(res.getString("nicklist.popup.hoperators"));
 				cm.addActionListener(this);
 				mi = new MenuItem(res.getString("nicklist.popup.kick"));
 				mi.setActionCommand("KICK");
 				cm.add(mi);
 				mi = new MenuItem(res.getString("nicklist.popup.kban"));
 				mi.setActionCommand("KBAN");
 				cm.add(mi);
 
 				cm.addSeparator();
 				mi = new MenuItem(res.getString("nicklist.popup.ban"));
 				mi.setActionCommand("MODE {1} +b {0}");
 				cm.add(mi);
 				mi = new MenuItem(res.getString("nicklist.popup.unban"));
 				mi.setActionCommand("MODE {1} -b {0}");
 				cm.add(mi);
 
 				cm.addSeparator();
 				mi = new MenuItem(res.getString("nicklist.popup.except"));
 				mi.setActionCommand("MODE {1} +e {0}");
 				cm.add(mi);
 
 				cm.addSeparator();
 				mi = new MenuItem(res.getString("nicklist.popup.voice"));
 				mi.setActionCommand("MODE {1} +v {0}");
 				cm.add(mi);
 				mi = new MenuItem(res.getString("nicklist.popup.unvoice"));
 				mi.setActionCommand("MODE {1} -v {0}");
 				cm.add(mi);
 				
 				pm.add(cm);
 			}
 		}
 		if (eirc.isIRCop())
 		{
 			pm.addSeparator();
 			im = new Menu(res.getString("nicklist.popup.ircops"));
 			im.addActionListener(this);
 			pm.add(im);
 
 			mi = new MenuItem(res.getString("nicklist.popup.kill"));
 			mi.setActionCommand("KILL");
 			im.add(mi);
 		}
 		for (int i = 0; i &lt; pm.getItemCount(); i++)
 		{
 			pm.getItem(i).setFont(getFont());
 			pm.getItem(i).setEnabled(b);
 		}
 		for (int i = 0; i &lt; cm.getItemCount(); i++)
 		{
 			cm.getItem(i).setFont(getFont());
 			cm.getItem(i).setEnabled(b);
 		}
 		for (int i = 0; i &lt; im.getItemCount(); i++)
 		{
 			im.getItem(i).setFont(getFont());
 			im.getItem(i).setEnabled(b);
 		}
 	}
 
 	public void actionPerformed(ActionEvent ev)
 	{
 		Object comp = ev.getSource();
 
 		if (comp instanceof MenuItem)
 		{
 			String command = ev.getActionCommand();
 
 			if (command.indexOf("+b") >= 0 || command.indexOf("-b") >= 0)
 			{
 				// Ban by ip, or user, or finally nick

 
 				String mask = item_under_menu;
 				String addr = NickInfo.getInetAddr(item_under_menu);
 				String user = NickInfo.getUser(item_under_menu);
 				if (addr == null)
 				{
 					if (user != null)
 						mask = "*!" + user + "@*";
 				}
 				else
 				{
 					mask = "*!*@" + addr;
 				}
 
 				Object o[] = { mask, channel.getTag() };
 				MessageFormat mf = new MessageFormat(command);
 				eirc.sendCommand(mf.format(o), this);
 			}
 			else
 			{
 				Object o[] = { item_under_menu, channel.getTag() };
 				MessageFormat mf = new MessageFormat(command);
 
 				if (command.indexOf("KICK") >= 0 || command.indexOf("KBAN") >= 0)
 					eirc.openKick(command, channel.getTag(), item_under_menu, this);
 				else
 					if (command.indexOf("KILL") >= 0)
 						eirc.openKill(command, item_under_menu, this);
 					else
 						if (command.indexOf("GHOST") >= 0)
 							eirc.openGhostWin(command, item_under_menu, this);
 						else
 							eirc.sendCommand(mf.format(o), this);
 			}
 		}
 		else if (comp.equals(topic))
 		{
 			String p[] = { channel.getTag(), topic.getText() };
 			eirc.sendMessage("TOPIC", p);
 		}
 		else if (comp.equals(nick_list))
 		{
 			String item = ev.getActionCommand();
 			eirc.openPrivate(item);
 			eirc.showPanel(item);
 		}
 		else if (comp.equals(entry))
 		{
 			String text = entry.getText();
 			if (text.length() <= 0)
 				return;
 
 			// See if it's a command.

 			if (text.charAt(0) == '/')
 			{
 				// Remove slash.

 				text = text.substring(1);
 
 				// Avoid empty (or pure white space) input.

 				if (text.trim().length() > 0)
 					eirc.sendCommand(text, this);
 			}
 			else
 			{
 				if (text.length() > 450)
 					text = text.substring(0, 449);
 				if (color.getSelectedIndex() != 1)
 					text = MircMessage.COLOR + String.valueOf(color.getSelectedIndex()) + " " + text;
 
 				String [] p = { channel.getTag(), text };
 				eirc.sendMessage("PRIVMSG", p);
 
 				printMyPrivmsg(text, getChannel().get(getNick()));
 			}
 
 			entry.setText("");
 		}
 		else if (comp.equals(kill))
 		{
 			Object[] selection = nick_list.getSelectedObjects();
 			boolean selected = false;
 			String s = "";
 
 			for (int i = 0; i < selection.length; i++)
 			{
 				if (selected)
 					s = s.concat(",");
 				selected = true;
 				s = s.concat(selection[i].toString());
 			}
 
 			// Was a nick selected?

 			if (selected)
 			{
 				if (comp.equals(kill))
 					eirc.sendCommand("IGNORE " + s, this);
 			}
 			else
 			{
 				printError(res.getString("eirc.select_nicks"));
 			}
 		}
 		else if (comp.equals(close))
 		{
 			String p[] = { channel.getTag() };
 			eirc.sendMessage("PART", p);
 			eirc.closeChannel(channel.getTag());
 		}
 	}
 
 	public void itemStateChanged(ItemEvent e)
 	{
 		if (e.getSource() == color)
 			entry.requestFocus();
 	}
 
 	public void mouseClicked(MouseEvent ev)
 	{
 	}
 
 	public void mouseReleased(MouseEvent ev)
 	{
 	}
 
 	public void mousePressed(MouseEvent ev)
 	{
 		// Following code is for the NickList.

 		if (ev.isPopupTrigger() || (ev.getModifiers() & MouseEvent.BUTTON3_MASK) != 0)
 		{
 			// WORKAROUND: #27 MSIE doesn't set popupTrigger.

 			String item = nick_list.getNickAt(ev.getPoint());
 			if (item != null)
 			{
 				item_under_menu = item;
 				Point offset = nick_list.getScrollPosition();
 				popup_menu.show(nick_list, offset.x, offset.y);
 			}
 		}
 	}
 
 	public void mouseEntered(MouseEvent ev)
 	{
 	}
 
 	public void mouseExited(MouseEvent ev)
 	{
 	}
 
 	public void keyPressed(KeyEvent ev)
 	{
 		if (ev.getSource() == entry)
 		{
 			switch (ev.getKeyCode())
 			{
 			case (KeyEvent.VK_TAB):
 				ev.consume();
 				completeNick();
 				break;
 			default:
 				resetCompleteNick();
 				break;
 			}
 		}
 	}
 
 	public void keyReleased(KeyEvent ev)
 	{
 	}
 
 	public void keyTyped(KeyEvent ev)
 	{
 	}
 
 	public void setFocusTraversalKeysEnabled(TextField tf, boolean b)
 	{
 		try
 		{
 			Class FieldClass = tf.getClass();
 			Class args[] = { boolean.class };
 			Method setfocus = FieldClass.getMethod("setFocusTraversalKeysEnabled", args);
 			Object params[] = { new Boolean(b) };
 			Object obj = setfocus.invoke(tf, params);
 		}
 		catch (Exception ex) {}
 	}
 }