Home > Articles > Programming > Java

  • Print
  • + Share This
Like this article? We recommend

Like this article? We recommend

Move Columns from the Keyboard

If you have worked with Swing table components for a while, you probably are aware of a feature by which you can use the mouse to drag one column to another column's position. You accomplish that task by positioning the mouse cursor's hotspot over the header of the column to be dragged and then drag that column to another column's position. Figure 4 illustrates a situation in which columns B and C have been exchanged through dragging.

Figure 4 You can use the mouse to drag one column (such as C) to another column's (such as B's) position.

Although you can move columns to various column positions with the mouse, have you ever wanted to move columns from the keyboard? If so, you would be hard pressed to find a table component keystroke combination that accomplishes that task. When I first confronted that challenge, I decided to find out how the table component's dragging action results in column movement, to see if I could use the keyboard to duplicate that dragging action. However, after studying a lot of intricate code, I realized that there had to be an easier way to move columns. As I discovered, an easier way exists. That way involves the use of JTable's moveColumn() method to programmatically move columns. Before I go into detail on adding keyboard-initiated column movement support (via moveColumn()) to a table component, take a look at Listing 4's MoveColumns source code.

Listing 4: MoveColumns.java

// MoveColumns.java

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.table.*;

class MoveColumns extends JFrame
{
  MoveColumns (String title)
  {
   // Pass the title to the JFrame superclass so that it appears in
   // the title bar.

   super (title);

   // Tell the program to exit when the user either selects Close
   // from the System menu or presses an appropriate X button on the
   // title bar.

   setDefaultCloseOperation (EXIT_ON_CLOSE);

   // Create a data model for use with a JTable component.

   TableModel dataModel = new AbstractTableModel ()
   {
     final static int NROWS = 10;
     final static int NCOLS = 5;

     Object [][] rowData = new Object [NROWS] [];

     {
      for (int i = 0; i < NROWS; i++)
         rowData [i] = new Object [NCOLS];

      int x = 0;
      for (int row = 0; row < NROWS; row++)
         for (int col = 0; col < NCOLS; col++)
           rowData [row] [col] = new Integer (x++);
     }

     public int getColumnCount ()
     {
      return NCOLS;
     }

     public int getRowCount ()
     {
      return NROWS;
     }

     public Object getValueAt (int row, int col)
     {
      return rowData [row] [col];
     }

     public boolean isCellEditable (int row, int col)
     {
      if (row == 0 || col == 0)
        return true;

      return false;
     }

     public void setValueAt (Object value, int row, int col)
     {
      rowData [row] [col] = value;
      fireTableCellUpdated (row, col);
     }
   };

   // Create a JTable component that uses the previously created
   // data model. Assign a preferred size to this component and set
   // the cell at row 0, column 0 as the selected cell.

   final JTable jt = new JTable (dataModel);
   jt.setPreferredScrollableViewportSize (new Dimension (300, 180));
   jt.setColumnSelectionInterval (0, 0);
   jt.setRowSelectionInterval (0, 0);

   // Create a KeyStroke object associated with the Ctrl+left arrow
   // keystroke sequence and associate that keystroke sequence with
   // the MoveLeft action in the JTable's action table.

   KeyStroke ksLeft = KeyStroke.getKeyStroke (KeyEvent.VK_LEFT,
                         Event.CTRL_MASK);
   jt.getInputMap ().put (ksLeft, "MoveLeft");

   // Create a replacement MoveLeft action that obtains the 
   // currently selected column in the table (by calling
   // getSelectedColumn() and (if the column is not the leftmost
   // column) moves that column one
   // position to the left.

   Action moveLeft = new AbstractAction ()
            {
              public void actionPerformed (ActionEvent e)
              {
                int index = jt.getSelectedColumn ();
                if (index == 0)
                  return;
                jt.moveColumn (index, index - 1);
              }
            };

   // Replace the table's default MoveLeft action with the new
   // MoveLeft action.

   jt.getActionMap ().put ("MoveLeft", moveLeft);

   // Create a KeyStroke object associated with the Ctrl+right arrow
   // keystroke sequence, and associate that keystroke sequence with
   // the MoveRight action in the JTable's action table.

   KeyStroke ksRight = KeyStroke.getKeyStroke (KeyEvent.VK_RIGHT,
                         Event.CTRL_MASK);
   jt.getInputMap ().put (ksRight, "MoveRight");

   // Create a replacement MoveRight action that obtains the
   // currently selected column in the table (by calling
   // getSelectedColumn() and (if the column is not the rightmost
   // column) moves that column one position to the right.
   
   Action moveRight = new AbstractAction ()
             {
               public void actionPerformed (ActionEvent e)
               {
                int index = jt.getSelectedColumn ();
                if (index == jt.getColumnCount () - 1)
                  return;
                jt.moveColumn (index, index + 1);
               }
             };

   // Replace the table's default MoveRight action with the new
   // MoveRight action.

   jt.getActionMap ().put ("MoveRight", moveRight);

   // Place the table in a JScrollPane object (to allow the table to
   // be vertically scrolled and display scrollbars, as necessary).

   JScrollPane jsp = new JScrollPane (jt);

   // Add the JScrollPane object to the frame window's content pane.
   // That allows the table to be displayed within a displayed 
   // scroll pane.

   getContentPane ().add (jsp);

   // Size the frame window to the preferred size of its scroll pane
   // container.

   pack ();

   // Display the frame window and all contained
   // components/containers.

   setVisible (true);
  }

  public static void main (String [] args)
  {
   // Create a MoveColumns object, which creates the GUI.

   new MoveColumns ("Move Columns");
  }
}

MoveColumns chooses those keys that will be used to move columns. The Ctrl+left key combination is chosen to move columns left and recorded in an object created from the KeyStroke class (located in the javax.swing package), by way of KeyStroke ksLeft = KeyStroke.getKeyStroke (KeyEvent.VK_LEFT, Event.CTRL_MASK);. Similarly, the Ctrl+right key combination is chosen to move columns right and recorded in a KeyStroke object, by way of KeyStroke ksRight = KeyStroke.getKeyStroke (KeyEvent.VK_RIGHT, Event.CTRL_MASK);.

Once each KeyStroke object has been created, that object is mapped to the name of an action (an object whose actionPerformed() method gets called when the user presses one or more keys corresponding to its associated KeyStroke object) in a table component's input map—an object that associates Keystroke objects to the names of actions. When keys are pressed, the table component responds to those key presses by creating appropriate Keystroke objects, searching its input map for the names of corresponding actions, and using those names to continue its search for appropriate Action objects.

An Action object is an object whose class either directly or indirectly implements the Action interface (located in package javax.swing). The simplest way to create an Action object is to subclass the AbstractAction class (in the javax.swing package) and override the actionPerformed() method. Once an Action object has been created, the previously chosen action name is mapped to that object in a table component's action map—an object that associates String objects identifying action names with Action objects identifying actions. When the table component finds a matching Keystroke object in its input map, it takes the associated action name and uses that name to search its action map. Once it finds the appropriate entry, it retrieves the Action object's reference and calls that object's actionPerformed() method.

Within actionPerformed(), a call is made to getSelectedColumn(), to identify the column that contains the currently selected cell. If that method returns -1, there is no currently selected cell and actionPerformed() exits. Otherwise, a call is made to JTable's moveColumn() method with getSelectedColumn()'s returned value to move the column one position to the left or right (depending on the keystroke to which the action binds). And that is how you move columns from the keyboard.

TIP

To learn more about action and input maps, I invite you to read the Swing Connection article "Keyboard Bindings in Swing.".

  • + Share This
  • 🔖 Save To Your Account