// MultiColumn // A List box that supports multiple columns. import java.awt.*; import java.util.Vector; public class MultiColumn extends BorderPanel implements CbScrollbarCallback { MultiColumnCallback callback; // what to call back to String title[]; // column titles boolean adjustable = true; boolean drawlines = true; Color colors[][] = null; boolean enabled = true; boolean multiselect = false; int cpos[]; // column x positions float cwidth[]; // proportional column widths Vector list[]; // columns of the list CbScrollbar sb; // scrollbar at the right side int width, height; // size, minus the scrollbar Insets in; // used space around the border int sbwidth; // width of the scrollbar int th; // height of title bar Image bim; // backing image Graphics bg; // backing graphics Font font = new Font("timesRoman", Font.PLAIN, 12); FontMetrics fnm; // drawing font size int coldrag = -1; // column being resized int sel = -1; // selected row int sels[] = new int[0]; // all selected rows int top = 0; // first row displayed long last; // last mouse click time int rowh = 16; // row height Event last_event; // last event that triggered callback int sortcol; // Column currently being sorted int sortdir; // Sort direction (0=none, 1=up, 2=down) // Create a new list with the given column titles MultiColumn(String t[]) { super(3, Util.dark_edge_hi, Util.body_hi); title = new String[t.length]; for(int i=0; i= top+r) { top = s-1; if (top > list[0].size() - r) top = list[0].size() - r; sb.setValue(top); repaint(); } } // deleteItem // Remove one row from the list void deleteItem(int n) { for(int i=0; i 0) { System.arraycopy(sels, 0, nsels, 0, i); System.arraycopy(sels, i+1, nsels, i, nsels.length-i); sel = nsels[0]; } break; } } repaint(); compscroll(); } // clear // Remove everything from the list void clear() { for(int i=0; i= top && sels[i] <= bot) { bg.setColor(sels[i] == sel ? Util.body : lighterGray); bg.fillRect(0, th+(sels[i]-top)*rowh, width, rowh); } } } // Draw each column for(int i=0; i w-3) s = s.substring(0, s.length()-1); if (!enabled) bg.setColor(Util.body); else if (colors != null) bg.setColor(colors[j][i]); bg.drawString(s, x+1, th+(j+1-top)*rowh-fd); } else if (o instanceof Image) { // Render image in column Image im = (Image)o; bg.drawImage(im, x+1, th+(j-top)*rowh, this); } } } } // mouseDown // Select a list item or a column to drag public boolean mouseDown(Event e, int x, int y) { if (!enabled) { return true; } x -= in.left; y -= in.top; coldrag = -1; if (y < th) { // Click in title bar for(int i=0; i 0 && Math.abs(cpos[i] - x) < 3) { // clicked on a column separator coldrag = i; } else if (x >= cpos[i] && x < cpos[i+1]) { // clicked in a title callback.headingClicked(this, i); } } } else { // Item chosen from list int row = (y-th)/rowh + top; if (row < list[0].size()) { // Double-click? boolean dclick = false; if (e.when-last < 1000 && sel == row) dclick = true; else last = e.when; if (e.shiftDown() && multiselect && sel != -1) { // Select all from last selection to this one int zero = sels[0]; if (zero < row) { sels = new int[row-zero+1]; for(int i=zero; i<=row; i++) sels[i-zero] = i; } else { sels = new int[zero-row+1]; for(int i=zero; i>=row; i--) sels[zero-i] = i; } } else if (e.controlDown() && multiselect) { // Add this one to selection int nsels[] = new int[sels.length + 1]; System.arraycopy(sels, 0, nsels, 0,sels.length); nsels[sels.length] = row; sels = nsels; } else { // Select one row only, and de-select others sels = new int[1]; sels[0] = row; } sel = row; repaint(); last_event = e; if (callback != null) { // Callback the right function if (dclick) callback.doubleClick(this, row); else callback.singleClick(this, row); } else { // Send an event getParent().postEvent( new Event(this, Event.ACTION_EVENT, dclick?"Double":"Single")); } } } return true; } // mouseDrag // If a column is selected, change it's width public boolean mouseDrag(Event e, int x, int y) { if (!enabled) { return true; } x -= in.left; y -= in.top; if (coldrag != -1) { if (x > cpos[coldrag-1]+3 && x < cpos[coldrag+1]-3) { cpos[coldrag] = x; cwidth[coldrag-1] = (cpos[coldrag]-cpos[coldrag-1]) / (float)width; cwidth[coldrag] = (cpos[coldrag+1]-cpos[coldrag]) / (float)width; repaint(); } } return true; } public void moved(CbScrollbar s, int v) { moving(s, v); } public void moving(CbScrollbar s, int v) { top = sb.getValue(); compscroll(); repaint(); } // compscroll // Re-compute the size of the scrollbar private void compscroll() { if (fnm == null) return; // not visible int r = rows(); int c = list[0].size() - r; sb.setValues(top, r==0?1:r, list[0].size()); } // rows // Returns the number of rows visible in the list private int rows() { return Math.min(height/rowh - 1, list[0].size()); } public Dimension minimumSize() { return new Dimension(400, 100); } public Dimension preferredSize() { return minimumSize(); } } // MultiColumnCallback // Objects implementing this interface can be passed to the MultiColumn // class, to have their singleClick() and doubleClick() functions called in // response to single or double click in the list. interface MultiColumnCallback { // singleClick // Called on a single click on a list item void singleClick(MultiColumn list, int num); // doubleClick // Called upon double-clicking on a list item void doubleClick(MultiColumn list, int num); // headingClicked // Called when a column heading is clicked on void headingClicked(MultiColumn list, int col); }