/*
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
*/
package ar.com.jkohen.awt;
import java.awt.*;
import java.awt.event.*;
import java.text.Collator;
import java.text.CollationKey;
import java.util.Enumeration;
import java.util.Vector;
import ar.com.jkohen.irc.RFC1459;
import ar.com.jkohen.irc.User;
import ar.com.jkohen.awt.event.NewMouseWheelEvent;
public class NickList extends Panel implements MouseListener, MouseMotionListener, ItemSelectable
{
public static final int SYMBOL_RENDERER = 0;
public static final int BULLET_RENDERER = 1;
public static final int SORT_ALPHA = 0;
public static final int SORT_ALPHA_FAVOR_MODE = 1;
private static final int PREF_MIN_WIDTH = 190;
private int item_renderer = BULLET_RENDERER;
private int sort_method = SORT_ALPHA;
private MouseListener mouseListener;
private ActionListener actionListener;
private ItemListener itemListener;
protected NickListArea screen_list;
protected Scrollbar sb;
private NickInfoPopup nick_pop;
private Point mouse_coords;
protected Vector items;
private Collator collator;
private Color textbg = SystemColor.text;
private Color textfg = SystemColor.textText;
private Color selbg = SystemColor.textHighlight;
private Color selfg = SystemColor.textHighlightText;
private String last_selected_nick;
private static boolean hasWindowFocus = true;
public NickList()
{
collator = RFC1459.getCollator(sort_method);
items = new Vector();
setLayout(new BorderLayout(0, 0));
sb = new Scrollbar(Scrollbar.VERTICAL);
//sb.setBackground(SystemColor.text);
add("East", sb);
screen_list = new NickListArea(sb);
add("Center", screen_list);
nick_pop = new NickInfoPopup();
add("West", nick_pop);
screen_list.addMouseListener(this);
screen_list.addMouseMotionListener(this);
enableWheelEvt(true);
updateBar();
}
public String[] getNicks()
{
String list[] = new String [items.size()];
int i = 0;
for (Enumeration e = items.elements(); e.hasMoreElements(); )
list[i++] = ((NickItem)e.nextElement()).getNick();
return list;
}
/**
* Gets the nick name for the item that contains p.
*
* @param p the point to look for in the nicks list.
*/
public String getNickAt(Point p)
{
NickItem ni = getNickItemAt(p);
if (ni != null)
return ni.getNick();
else
return(null);
}
/**
* Gets the NickItem that contains p.
*
* @param p the point to look for in the NickList.
*/
protected NickItem getNickItemAt(Point p)
{
for (int i = screen_list.offset; i < items.size(); i++ )
{
NickItem ni = (NickItem)items.elementAt(i);
Rectangle r = new Rectangle(ni.getBounds().x, ni.getBounds().y, screen_list.getBounds().width, ni.getBounds().height);
if (r.contains(p))
return ni;
}
return null;
}
public void loadList(User users[])
{
items.removeAllElements();
for (int i = 0; i < users.length; i++)
add(users[i]);
repaint();
}
/**
* Adds nick to the list with user mode mode.
*/
public void add(User u)
{
if (u == null)
throw new IllegalArgumentException("Invalid User");
String nick = u.getTag();
int mode = u.getModes();
NickItem ni = new NickItem(nick);
switch (item_renderer)
{
case BULLET_RENDERER:
ni.setBullet(true);
break;
case SYMBOL_RENDERER:
ni.setBullet(false);
break;
default:
throw new IllegalArgumentException("Invalid NickItem renderer selected");
}
ni.setForeground(getForeground());
ni.setBackground(getBackground());
ni.setSelectedForeground(selfg);
ni.setSelectedBackground(selbg);
ni.setModes(mode);
ni.setTextBackground(textbg);
if ((mode & User.OWNER_MASK) != 0)
ni.setTextForeground(User.COLOR_OWNER);
else if ((mode & User.ADMIN_MASK) != 0)
ni.setTextForeground(User.COLOR_ADMIN);
else if ((mode & User.OP_MASK) != 0)
ni.setTextForeground(User.COLOR_OP);
else if ((mode & User.HALFOP_MASK) != 0)
ni.setTextForeground(User.COLOR_HOP);
else if ((mode & User.VOICE_MASK) != 0)
ni.setTextForeground(User.COLOR_VOICE);
else
ni.setTextForeground(NickInfo.nickToColor(nick));
/* Sort at insert */
nick = ni.toString();
for (int i = 0; i < items.size(); i++)
{
Object o = items.elementAt(i);
int comp = collator.compare(nick, o.toString());
if (comp < 0)
{
items.insertElementAt(ni, i);
return;
}
else if (comp == 0)
{
items.setElementAt(ni, i);
return;
}
}
items.addElement(ni);
}
public void remove(String u)
{
if (u == null)
throw new IllegalArgumentException("Invalid User");
for (int i = 0; i < items.size(); i++)
{
NickItem ni = (NickItem)items.elementAt(i);
int comp = collator.compare(u, ni.getNick());
if (comp == 0)
{
items.removeElementAt(i);
return;
}
}
}
public void update(String u1, User u2)
{
remove(u1);
add(u2);
}
protected void setItemSelected(NickItem ni, boolean state)
{
ni.setSelected(state);
int item_state = state ? ItemEvent.SELECTED : ItemEvent.DESELECTED;
processItemEvent(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this, item_state));
screen_list.repaint();
}
protected void unselectAllItems()
{
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
setItemSelected(ni, false);
}
}
public int getSortMethod()
{
return sort_method;
}
public synchronized void setSortMethod(int method)
{
this.sort_method = method;
collator = RFC1459.getCollator(sort_method);
}
public String getSelectedItem()
{
return last_selected_nick;
}
public Object[] getSelectedObjects()
{
Vector temp_list = new Vector(items.size());
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
if (ni.isSelected())
temp_list.addElement(ni.getNick());
}
Object[] objs = new String[temp_list.size()];
temp_list.copyInto(objs);
return objs;
}
public int getItemRenderer()
{
return item_renderer;
}
public void setItemRenderer(int item_renderer)
{
this.item_renderer = item_renderer;
}
public void repaint()
{
screen_list.repaint();
updateBar();
}
public void setFont(Font f)
{
screen_list.setFont(f);
nick_pop.setFont(f);
repaint();
}
public void setBackground(Color c)
{
screen_list.setBackground(c);
repaint();
}
public Color getTextBackground()
{
return(textbg);
}
public void setTextBackground(Color c)
{
screen_list.setBackground(c);
this.textbg = c;
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
ni.setTextBackground(c);
}
repaint();
}
public Color getTextForeground()
{
return textfg;
}
public void setTextForeground(Color c)
{
this.textfg = c;
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
ni.setTextForeground(c);
}
}
public Color getSelectedBackground()
{
return selbg;
}
public void setSelectedBackground(Color c)
{
this.selbg = c;
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
ni.setSelectedBackground(c);
}
}
public Color getSelectedForeground()
{
return selfg;
}
public void setSelectedForeground(Color c)
{
this.selfg = c;
for (Enumeration e = items.elements(); e.hasMoreElements(); )
{
NickItem ni = (NickItem) e.nextElement();
ni.setSelectedForeground(c);
}
}
public Dimension getPreferredSize()
{
Dimension d = super.getPreferredSize();
d.width = Math.min(d.width, PREF_MIN_WIDTH);
return(d);
}
public static void setWindowFocus(boolean b)
{
NickList.hasWindowFocus = b;
}
public Point getScrollPosition()
{
return(mouse_coords);
}
public void updateBar()
{
if (sb == null)
return;
if (screen_list != null)
{
int size = items.size();
int visible = screen_list.getVisible();
if (size >= visible)
{
sb.setValues(sb.getValue(), visible, 0, size);
sb.setBlockIncrement(size / 10);
return;
}
}
sb.setValues(0, 0, 0, 0);
sb.setBlockIncrement(0);
}
/**
* Translate the event as if it were thrown by this object.
*/
protected MouseEvent translateListMouseEvent(MouseEvent ev)
{
return new MouseEvent(this, ev.getID(), ev.getWhen(), ev.getModifiers(), ev.getX(), ev.getY(), ev.getClickCount(), ev.isPopupTrigger());
}
public void addMouseListener(MouseListener l)
{
if (null != l)
mouseListener = AWTEventMulticaster.add(mouseListener, l);
}
public void removeMouseListener(MouseListener l)
{
if (null != l)
mouseListener = AWTEventMulticaster.remove(mouseListener, l);
}
public void addActionListener(ActionListener l)
{
if (null != l)
actionListener = AWTEventMulticaster.add(actionListener, l);
}
public void removeActionListener(ActionListener l)
{
if (null != l)
actionListener = AWTEventMulticaster.remove(actionListener, l);
}
protected void processActionEvent(ActionEvent e)
{
if (actionListener != null)
actionListener.actionPerformed(e);
}
public void addItemListener(ItemListener l)
{
if (null != l)
itemListener = AWTEventMulticaster.add(itemListener, l);
}
public void removeItemListener(ItemListener l)
{
if (null != l)
itemListener = AWTEventMulticaster.remove(itemListener, l);
}
protected void processItemEvent(ItemEvent e)
{
if (itemListener != null)
itemListener.itemStateChanged(e);
}
public void mouseClicked(MouseEvent ev)
{
Object source = ev.getSource();
if (source.equals(screen_list))
{
if (mouseListener != null)
mouseListener.mouseClicked(translateListMouseEvent(ev));
NickItem ni = getNickItemAt(ev.getPoint());
if (null == ni)
return;
int ev_modifiers = ev.getModifiers();
if ((ev_modifiers & MouseEvent.BUTTON1_MASK) != 0 || ev_modifiers == 0)
{
// WORKAROUND: #32 Netscape Navigator doesn't set it right for BUTTON1.
String nick = ni.getNick();
switch (ev.getClickCount())
{
case 1:
boolean previously_selected = ni.isSelected();
if (!previously_selected)
last_selected_nick = nick;
if (0 == (ev_modifiers & MouseEvent.CTRL_MASK))
unselectAllItems();
setItemSelected(ni, !previously_selected);
break;
case 2:
processActionEvent(new ActionEvent(NickList.this, ActionEvent.ACTION_PERFORMED, nick, ev.getModifiers()));
break;
}
}
}
}
public void mousePressed(MouseEvent ev)
{
Object source = ev.getSource();
if (source.equals(screen_list))
if (mouseListener != null)
mouseListener.mousePressed(translateListMouseEvent(ev));
}
public void mouseReleased(MouseEvent ev)
{
Object source = ev.getSource();
if (source.equals(screen_list))
if (mouseListener != null)
mouseListener.mouseReleased(translateListMouseEvent(ev));
}
public void mouseEntered(MouseEvent ev)
{
if (!hasWindowFocus || !isShowing())
return;
Object source = ev.getSource();
if (source.equals(this))
if (mouseListener != null)
mouseListener.mouseEntered(ev);
}
public void mouseExited(MouseEvent ev)
{
Object source = ev.getSource();
if (source.equals(this))
{
if (mouseListener != null)
mouseListener.mouseExited(ev);
}
else if (source.equals(screen_list))
{
mouse_coords = null;
screen_list.repaint();
}
}
public void mouseMoved(MouseEvent ev)
{
if (!hasWindowFocus || !isShowing())
return;
Object source = ev.getSource();
if (source.equals(screen_list))
{
mouse_coords = ev.getPoint();
screen_list.repaint();
}
}
public void mouseDragged(MouseEvent ev)
{
}
public void mouseWheelMoved(NewMouseWheelEvent ev)
{
if (ev != null)
{
mouse_coords = ev.getPoint();
screen_list.mouseWheelMoved(ev);
repaint();
}
}
public void processEvent(AWTEvent ev)
{
if (ev.getClass() == NewMouseWheelEvent.EventClass)
mouseWheelMoved(new NewMouseWheelEvent((MouseEvent)ev));
super.processEvent(ev);
}
public void enableWheelEvt(boolean b)
{
if (b && NewMouseWheelEvent.EventMask > 0)
enableEvents(NewMouseWheelEvent.EventMask);
else
disableEvents(NewMouseWheelEvent.EventMask);
}
protected class NickListArea extends Canvas implements AdjustmentListener
{
private Image img;
private Graphics gph;
private Scrollbar bar;
private int offset, this_w, this_h, img_w, img_h, line_h, adjust, old_num, visible;
private int border_w = 2, border_h = 2;
public NickListArea()
{
this(null);
}
public NickListArea(Scrollbar sb)
{
bar = sb;
bar.addAdjustmentListener(this);
}
public void setBackground(Color c)
{
super.setBackground(c);
img = null;
this.repaint();
}
private void createBuffer(int w, int h)
{
this_w = w;
this_h = h;
img_w = this_w;
img_h = this_h;
img = createImage(img_w, img_h);
if (img != null)
gph = img.getGraphics();
if (NewGraphics2D.hasRenderingHints)
NewGraphics2D.setRendering(gph);
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
int size = getLinesNum();
int w = getSize().width;
int h = getSize().height;
visible = 0;
/* Component resized */
if ((img == null || w != this_w || h != this_h) && w > 0 && h > 0)
{
createBuffer(w, h);
if (line_h > 0 && line_h < img_h)
visible = img_h / line_h;
updateBar();
}
gph.clearRect(0, 0, img_w, img_h);
if (size > 0)
{
NickItem ni = (NickItem)items.elementAt(0);
Dimension d = ni.getSize(g);
line_h = d.height;
adjust = 0;
if (line_h > 0 && line_h < img_h)
visible = img_h / line_h;
if (((size * line_h) > img_h) && ((size - offset) * line_h) < img_h)
{
offset = size - (img_h / line_h);
if ((img_h % line_h) < (line_h / 2))
adjust = img_h % line_h;
}
// if ((size * line_h) < img_h)
// offset = 0;
paintText(gph);
paintRollover(gph);
}
paintBorder(gph);
if (img != null)
g.drawImage(img, 0, 0, this);
}
private void paintText(Graphics g) throws ArrayIndexOutOfBoundsException
{
Dimension d = null;
int y = border_h - adjust;
int i = offset;
while(i < items.size() && y < img_h)
{
NickItem ni = (NickItem)items.elementAt(i++);
if (ni != null)
{
d = ni.getSize(g);
ni.setBounds(0, y, img_h, d.height);
ni.paint(g);
y += d.height;
}
}
if (d != null)
line_h = d.height;
}
private void paintBorder(Graphics g)
{
g.setColor(Color.gray);
int tempx1[] = {0, 0, this_w - 1};
int tempy1[] = {this_h - 1, 0, 0};
g.drawPolyline(tempx1, tempy1, 3);
g.setColor(Color.black);
int tempx2[] = {1, 1, this_w - 2};
int tempy2[] = {this_h - 2, 1, 1};
g.drawPolyline(tempx2, tempy2, 3);
g.setColor(Color.white);
int tempx3[] = {0, this_w - 1, this_w - 1};
int tempy3[] = {this_h - 1, this_h - 1, 0};
g.drawPolyline(tempx3, tempy3, 3);
g.setColor(Color.lightGray);
int tempx4[] = {1, this_w - 2, this_w - 2};
int tempy4[] = {this_h - 2, this_h - 2, 1};
g.drawPolyline(tempx4, tempy4, 3);
}
private void paintRollover(Graphics g)
{
nick_pop.setVisible(false);
if (mouse_coords != null)
{
nick_pop.setLocation(mouse_coords);
NickItem ni = getNickItemAt(mouse_coords);
if (ni != null && ni.getNick() != null)
{
nick_pop.setNick(ni.getNick());
nick_pop.setVisible(true);
nick_pop.paint(g);
}
}
}
public int getLinesNum()
{
if (items != null)
return(items.size());
else
return(0);
}
public int getVisible()
{
return(visible);
}
public void adjustmentValueChanged(AdjustmentEvent e)
{
offset = e.getValue();
this.repaint();
}
public void mouseWheelMoved(NewMouseWheelEvent e)
{
int num = e.getUnitsToScroll();
if (num != 0)
{
offset = offset + num;
if (offset < 0)
offset = 0;
if (sb != null)
{
int max = sb.getMaximum() - sb.getVisibleAmount();
if (offset > max)
offset = max;
sb.setValue(offset);
}
// System.out.println("offset " + offset + " size " + getLinesNum() + " min " + bar.getMinimum() + " max " + bar.getMaximum() + " visible " + bar.getVisibleAmount());
this.repaint();
}
}
}
}