Logo Search packages:      
Sourcecode: webmin version File versions

MultiColumn.java

// 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<t.length; i++)
            title[i] = t[i];
      list = new Vector[t.length];
      for(int i=0; i<t.length; i++)
            list[i] = new Vector();
      cwidth = new float[t.length];
      for(int i=0; i<t.length; i++)
            cwidth[i] = 1.0f/t.length;
      cpos = new int[t.length+1];
      setLayout(null);
      sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
      add(sb);
      }

      // Create a new list that calls back to the given object on
      // single or double clicks.
      MultiColumn(String t[], MultiColumnCallback c)
      {
      this(t);
      callback = c;
      }

      // addItem
      // Add a row to the list
      void addItem(Object item[])
      {
      for(int i=0; i<title.length; i++)
            list[i].addElement(item[i]);
      repaint();
      compscroll();
      }

      // addItems
      // Add several rows to the list
      void addItems(Object item[][])
      {
      for(int i=0; i<item.length; i++)
            for(int j=0; j<title.length; j++)
                  list[j].addElement(item[i][j]);
      repaint();
      compscroll();
      }

      // modifyItem
      // Changes one row of the table
      void modifyItem(Object item[], int row)
      {
      for(int i=0; i<title.length; i++)
            list[i].setElementAt(item[i], row);
      repaint();
      compscroll();
      }

      // getItem
      // Returns the contents of a given row
      Object []getItem(int n)
      {
      Object r[] = new Object[title.length];
      for(int i=0; i<title.length; i++)
            r[i] = list[i].elementAt(n);
      return r;
      }

      // selected
      // Return the most recently selected row
      int selected()
      {
      return sel;
      }

      // select
      // Select some row
      void select(int s)
      {
      sel = s;
      sels = new int[1];
      sels[0] = s;
      repaint();
      }

      // select
      // Select multiple rows
      void select(int s[])
      {
      if (s.length == 0) {
            sel = -1;
            sels = new int[0];
            }
      else {
            sel = s[0];
            sels = s;
            }
      repaint();
      }

      // allSelected
      // Returns all the selected rows
      int[] allSelected()
      {
      return sels;
      }

      // scrollto
      // Scroll to make some row visible
      void scrollto(int s)
      {
      int r = rows();
      if (s < top || s >= 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<title.length; i++)
            list[i].removeElementAt(n);
      if (n == sel) {
            // De-select deleted file
            sel = -1;
            }
      for(int i=0; i<sels.length; i++) {
            if (sels[i] == n) {
                  // Remove from selection list
                  int nsels[] = new int[sels.length-1];
                  if (nsels.length > 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<title.length; i++)
            list[i].removeAllElements();
      sel = -1;
      sels = new int[0];
      top = 0;
      repaint();
      sb.setValues(0, 1, 0);
      }

      // setWidths
      // Set the proportional widths of each column
      void setWidths(float w[])
      {
      for(int i=0; i<title.length; i++)
            cwidth[i] = w[i];
      respace();
      repaint();
      }

      /**Turns on or off the user's ability to adjust column widths
       * @param a Can adjust or not?
       */
      void setAdjustable(boolean a)
      {
      adjustable = a;
      }

      /**Turns on or off the drawing of column lines
       * @param d Draw lines or not?
       */
      void setDrawLines(boolean d)
      {
      drawlines = d;
      }

      /**Sets the array of colors used to draw text items.
       * @param c The color array (in row/column order), or null to
       *          use the default
       */
      void setColors(Color c[][])
      {
      colors = c;
      repaint();
      }

      // Turns on or off multi-row selection with ctrl and shift
      void setMultiSelect(boolean m)
      {
      multiselect = m;
      }

      // Enables the entire list
      public void enable()
      {
      enabled = true;
      sb.enable();
      repaint();
      }

      // Disables the entire list
      public void disable()
      {
      enabled = false;
      sb.disable();
      repaint();
      }

      // Sets or turns off the sort indication arrow for a column
      // Direction 0 = None, 1 = Up arrow, 2 = Down arrow
      public void sortingArrow(int col, int dir)
      {
      sortcol = col;
      sortdir = dir;
      repaint();
      }

      public void setFont(Font f)
      {
      font = f;
      bim = null;
      repaint();
      }

      // Returns the total number of rows
      public int count()
      {
      return list[0].size();
      }

      // reshape
      // Called when this component gets resized
      public void reshape(int nx, int ny, int nw, int nh)
      {
      if (nw != width+sbwidth || nh != height) {
            in = insets();
            sbwidth = sb.minimumSize().width;
            width = nw-sbwidth - (in.left + in.right);
            height = nh - (in.top + in.bottom);
            sb.reshape(width+in.left, in.top, sbwidth, height);
            respace();

            // Force creation of a new backing image and re-painting
            bim = null;
            repaint();
            compscroll();
            }
      super.reshape(nx, ny, nw, nh);
      }

      // respace
      // Compute pixel column widths from proportional widths
      void respace()
      {
      cpos[0] = 0;
      for(int i=0; i<title.length; i++)
            cpos[i+1] = cpos[i] + (int)(width*cwidth[i]);
      }

      // paint
      // Blit the backing image to the front
      public void paint(Graphics g)
      {
      super.paint(g);
      if (bim == null) {
            // This is the first rendering
            bim = createImage(width, height);
            bg = bim.getGraphics();
            bg.setFont(font);
            fnm = bg.getFontMetrics();
            th = fnm.getHeight() + 4;
            render();
            compscroll();
            }
      g.drawImage(bim, in.left, in.top, this);
      }

      // update
      // Called sometime after repaint()
      public void update(Graphics g)
      {
      if (fnm != null) {
            render();
            paint(g);
            }
      }

      // render
      // Re-draw the list into the backing image
      void render()
      {
      int fh = fnm.getHeight(),     // useful font metrics
          fd = fnm.getDescent(),
          fa = fnm.getAscent();
      int bot = Math.min(top+rows()-1, list[0].size()-1);

      // Clear title section and list
      bg.setColor(Util.body);
      bg.fillRect(0, 0, width, th);
      bg.setColor(Util.light_bg);
      bg.fillRect(0, th, width, height-th);
      Color lighterGray = Util.body_hi;

      if (enabled) {
            // Mark the selected rows
            for(int i=0; i<sels.length; i++) {
                  if (sels[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<title.length; i++) {
            int x = cpos[i], w = cpos[i+1]-x-1;

            // Column title
            bg.setColor(Util.light_edge);
            bg.drawLine(x, 0, x+w, 0);
            bg.drawLine(x, 1, x+w-1, 1);
            bg.drawLine(x, 0, x, th-1);
            bg.drawLine(x+1, 0, x+1, th-2);
            bg.setColor(Util.dark_edge);
            bg.drawLine(x, th-1, x+w, th-1);
            bg.drawLine(x, th-2, x+w-1, th-2);
            bg.drawLine(x+w, th-1, x+w, 0);
            bg.drawLine(x+w-1, th-1, x+w-1, 1);
            int tw = fnm.stringWidth(title[i]);
            if (tw < w-6)
                  bg.drawString(title[i], x+(w-tw)/2, th-fd-2);

            // Sorting arrow
            int as = th-8;
            if (sortcol == i && sortdir == 1) {
                  bg.setColor(Util.light_edge);
                  bg.drawLine(x+4, th-5, x+4+as, th-5);
                  bg.drawLine(x+4+as, th-5, x+4+as/2, th-5-as);
                  bg.setColor(Util.dark_edge);
                  bg.drawLine(x+4+as/2, th-5-as, x+4, th-5);
                  }
            else if (sortcol == i && sortdir == 2) {
                  bg.setColor(Util.light_edge);
                  bg.drawLine(x+4+as/2, th-5, x+4+as, th-5-as);
                  bg.setColor(Util.dark_edge);
                  bg.drawLine(x+4, th-5-as, x+4+as, th-5-as);
                  bg.drawLine(x+4, th-5-as, x+4+as/2, th-5);
                  }

            // Column items
            if (drawlines) {
                  bg.setColor(Util.body);
                  bg.drawLine(x+w-1, th, x+w-1, height);
                  bg.setColor(Util.dark_edge);
                  bg.drawLine(x+w, th, x+w, height);
                  }
            for(int j=top; j<=bot; j++) {
                  Object o = list[i].elementAt(j);
                  if (o instanceof String) {
                        // Render string in column
                        String s = (String)o;
                        while(fnm.stringWidth(s) > 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<title.length; i++) {
                  if (adjustable && 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);
}


Generated by  Doxygen 1.6.0   Back to index