A lightweight Component. This List has some things the awt.List hasn't: This List is able to scroll to the end if the caller wants it. Also, we can scroll per pixel (and not per line). We can append characters and strings. Finally, each line can have a color associated.
package awt; import java.util.Vector; import java.awt.*; import java.awt.event.*; public class List extends Container implements AdjustmentListener,ItemSelectable { private ActionListener actionListener; private ItemListener itemListener; private Vector elements = new Vector(); private int selectedIdx = -1; private int longestLine = -1; private Scrollbar vertScroller,horScroller; public List() { enableEvents(AWTEvent.MOUSE_EVENT_MASK); vertScroller = new Scrollbar(Scrollbar.VERTICAL); horScroller = new Scrollbar(Scrollbar.HORIZONTAL); vertScroller.addAdjustmentListener(this); horScroller.addAdjustmentListener(this); add(vertScroller); add(horScroller); } public void addActionListener(ActionListener listener) { actionListener = AWTEventMulticaster.add(actionListener,listener); } public void addItemListener(ItemListener listener) { itemListener = AWTEventMulticaster.add(itemListener,listener); } public void removeItemListener(ItemListener listener) { itemListener = AWTEventMulticaster.remove(itemListener,listener); } public Object [] getSelectedObjects() { if (selectedIdx < 0) return null; String [] result = new String[1]; result[0] = getItem(selectedIdx); return result; } public String getSelectedItem() { if (selectedIdx < 0) return null; return getItem(selectedIdx); } public void setSelectedIndex(int index) { selectedIdx = index; vertScroller.setValue(index * getGraphics().getFontMetrics().getHeight()); repaint(); } public void processMouseEvent(MouseEvent e) { switch (e.getID()) { case MouseEvent.MOUSE_PRESSED: int oldselIdx = selectedIdx; selectedIdx = (e.getY()+vertScroller.getValue())/getGraphics().getFontMetrics().getHeight(); if (selectedIdx<0 || selectedIdx>=elements.size()) { if (oldselIdx == -1) break; if (itemListener != null) itemListener.itemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,new Integer(oldselIdx),ItemEvent.DESELECTED)); selectedIdx = -1; } else if (e.getClickCount() == 2) { if (actionListener != null) actionListener.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,"")); } if (selectedIdx >= 0) if (itemListener!=null && oldselIdx!=selectedIdx) itemListener.itemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,new Integer(selectedIdx),ItemEvent.SELECTED)); if (oldselIdx != selectedIdx) repaint(); break; } super.processMouseEvent(e); } public void setFont(Font font) { super.setFont(font); repaint(); } private void setScrollValues() { int fontHeight; { Graphics g = getGraphics(); if (g == null) { // postpone till paint() horScroller.setUnitIncrement(0); return; } fontHeight = g.getFontMetrics().getHeight(); } Dimension siz = getSize(); vertScroller.setValues(vertScroller.getValue(), siz.height-16,0, elements.size() * fontHeight); vertScroller.setBlockIncrement(((siz.height-16)*4)/5); vertScroller.setUnitIncrement(fontHeight); if (longestLine < 0) { FontMetrics fm = getGraphics().getFontMetrics(); for (int i = elements.size()-1; i>=0; i--) { int len = fm.stringWidth(((String)elements.elementAt(i)).substring(3)); if (len > longestLine) longestLine = len; } } horScroller.setValues(horScroller.getValue(),siz.width-16,0,longestLine); horScroller.setBlockIncrement(((siz.width-16)*4)/5); horScroller.setUnitIncrement(fontHeight); } public void add(String str,Color col) { add(str,col.getRed(),col.getGreen(),col.getBlue()); } public void add(String str,Color col,int pos) { add(str,col.getRed(),col.getGreen(),col.getBlue(),pos); } public void add(String str,int r,int g,int b) { add(str,r,g,b,-1); } public void add(String str,int r,int g,int b,int pos) { if (pos<0 || pos>elements.size()) pos = elements.size(); if (r<0) r=0; else if (r>255) r=255; if (g<0) g=0; else if (g>255) g=255; if (b<0) b=0; else if (b>255) b=255; Graphics gfx = getGraphics(); if (gfx == null) longestLine = -1; else { int len = gfx.getFontMetrics().stringWidth(str); if (len > longestLine) { longestLine = len; setScrollValues(); } } // the ""'s are necessary because otherwise we get one char containing r+g+b ... if (selectedIdx >= pos) selectedIdx++; elements.insertElementAt((char)r+""+(char)g+""+(char)b+str,pos); if (gfx != null) { setScrollValues(); repaint(); } } public void addItem(String str) { add(str,-1); } public void addItem(String str,int pos) { add(str,pos); } public void add(String str) { add(str,-1); } public void add(String str,int pos) { if (pos<0 || pos>elements.size()) pos = elements.size(); Graphics g = getGraphics(); if (g == null) longestLine = -1; else { int len = g.getFontMetrics().stringWidth(str); if (len > longestLine) { longestLine = len; setScrollValues(); } } if (selectedIdx >= pos) selectedIdx++; elements.insertElementAt("\uFFFF\0\0"+str,pos); if (g != null) { setScrollValues(); repaint(); } } public int getSelectedIndex() { return selectedIdx; } public int getItemCount() { return elements.size(); } public String getItem(int item) { return ((String)elements.elementAt(item)).substring(3); } public void remove(int position) { elements.removeElementAt(position); if (selectedIdx == position) selectedIdx = -1; else if (selectedIdx > position) selectedIdx--; repaint(); }
Remove first occurence of item
public void remove(String item) { for (int i = 0; i < elements.size(); i++) { if (getItem(i).equals(item)) { remove(i); return; } } } public void removeAll() { elements.setSize(0); selectedIdx = -1; longestLine = -1; setScrollValues(); repaint(); } public void doLayout() { Dimension siz = getSize(); vertScroller.setBounds(siz.width-16,0,16,siz.height-16); horScroller.setBounds(0,siz.height-16,siz.width-16,16); setScrollValues(); } public Graphics getGraphics() { // A lightweight container must change the font itself ... Graphics g = super.getGraphics(); if (g == null) return null; g.setFont(getFont()); return g; } private Image offscreen; public void invalidate() { super.invalidate(); offscreen = null; } public void paint(Graphics g) { if (offscreen == null) offscreen = createImage(getSize().width,getSize().height); Graphics onscreen = g; g = offscreen.getGraphics(); if (horScroller.getUnitIncrement() == 0) setScrollValues(); g.setFont(getFont()); // Sigh ... lightweight components must do everything by themselves ... int y = -vertScroller.getValue(); int lineHeight = g.getFontMetrics().getHeight(); int maxy = getSize().height-16; int ascent = g.getFontMetrics().getAscent(); int x = horScroller.getValue(); g.setClip(0,0,getSize().width-16,getSize().height-16); for (int pos = 0; pos < elements.size(); pos++,y+=lineHeight) { if (y+lineHeight < 0) continue; if (y > maxy) break; String str = (String)elements.elementAt(pos); if (str.charAt(0) == '\uFFFF') { // use default color g.setColor(pos==selectedIdx ? getForeground() : getBackground()); g.fillRect(0,y,getSize().width-16,lineHeight); g.setColor(pos==selectedIdx ? getBackground() : getForeground()); } else { // color rgb triple is in first 3 chars of the string if (pos == selectedIdx) g.setColor(new Color((int)str.charAt(0),(int)str.charAt(1),(int)str.charAt(2))); else g.setColor(getBackground()); g.fillRect(0,y,getSize().width-16,lineHeight); if (pos == selectedIdx) g.setColor(new Color(255-(int)str.charAt(0),255-(int)str.charAt(1),255-(int)str.charAt(2))); else g.setColor(new Color((int)str.charAt(0),(int)str.charAt(1),(int)str.charAt(2))); } g.drawString(str.substring(3),-x,y+ascent); } // erase lower part of display g.setColor(getBackground()); g.fillRect(0,y,getSize().width-16,maxy-y); onscreen.drawImage(offscreen,0,0,null); } public Dimension getMinimumSize() { return new Dimension(100,100); } public Dimension getPreferredSize() { return new Dimension(100,100); }
Internal version of append(), which does not cause a repaint
private void appendInternal(char c) { if (c == '\n') { elements.addElement("\uFFFF\0\0"); return; } longestLine = -1; if (elements.size() == 0) { elements.addElement("\uFFFF\0\0"+c); return; } int lines = elements.size(); String lastLine = (String)elements.elementAt(lines-1); elements.setElementAt(lastLine+=c,lines-1); setScrollValues(); } public void append(char c) { appendInternal(c); repaint(); } public void append(String str) { for (int i = 0; i < str.length(); i++) appendInternal(str.charAt(i)); repaint(); } public void update(Graphics g) { paint(g); } public void scrollToEnd() { setScrollValues(); // may not be updated cause of pending repaints()s, so do it now vertScroller.setValue(elements.size() * getGraphics().getFontMetrics().getHeight()); repaint(); } public void adjustmentValueChanged(AdjustmentEvent e) { repaint(); } }