Documents with Attributes

Java > Core SWING advanced programming > 7. TABLE EDITING > The Table Editing Process How It Works

 

The Table Editing Process How It Works

In the last section, you saw that double-clicking in an editable cell activates an editor that allows you to change the cell's contents and that to take advantage of this you only need to implement a suitable table data model. In many cases, the default editors that the table installs are sufficient for your needs, but having an understanding of the table editing process will help you to create custom editors that let you go beyond the basic capabilities that you've seen so far. In this section, you'll see how the editing process works; later in this chapter, you'll use this information to create a custom editor and to control the editing mechanism in such a way as to make it easier for the user to quickly make a large number of changes to a table.

Selecting an Editor

As with renderers, the table chooses the appropriate editor for each editable cell. The mechanism used to choose an editor is, in fact, the same as that used for renderers. At the top level, the following JTable method is invoked to select the editor for a given cell:

 public TableCellEditor getCellEditor (int row, int column); 

As was the case with renderers, you can subclass JTable and override this method to select a cell-specific editor if you need to. If you use the default implementation, the table first looks in the TableColumn object for the column containing the cell to be edited. If there is no specific renderer configured here, a default class-based editor is used instead. Because TableColumns do not have editors associated with them by default, a class-based editor will be used in any table that has not been specifically tailored. This is exactly the same process as the one used to select a cell's renderer.

Core Note

Should you choose to override the getCellEditor method, be sure to take into account that the column number refers to the TableColumnModel column index and not to the number of the column in the TableModel. If you need to refer to the TableModel data, you can map the column index passed to the getCellEditor method to the one needed to access the data using the JTable convertColumnIndexToModel method.



When it is instantiated, the table creates default editors for the following object types:

  • Booleans

  • Numbers

  • Objects

The editor for a column containing Boolean values is a JCheckBox, initialized as selected or not depending on whether the cell being edited contains true or false. When you click in an editable cell containing a Boolean value, the state of the checkbox is toggled and the new value is written back.

Numbers and Objects have a JTextField as their default editor. Both of these editors have a thin black border to clearly show the outline of the cell being edited, as you can see in Figure 7-1. The only difference between these two is that the Number editor shows its contents right-justified, while the Object editor is left-justified. By default, the object editor is used for any cell in columns for which the table model's getColumnClass method returns something other than Boolean.class or Number.class.

If you want to create a custom editor for specific classes, you can use the JTable setDefaultEditor method to associate the editor with the class of object it can handle; similarly, the getDefaultEditor method retrieves the editor for a given class:

 public void setDefaultEditor (Class objectClass,                               TableCellEditor editor); public TableCellEditor getDefaultEditor (Class objectClass); 

As with renderers, if the getDefaultEditor method does not find an exact match for a given class, it looks for an editor for the superclass and so on, until it finds a match. Because every class is ultimately derived from Object, as a last resort the editor for Object will be used to edit a cell whose content type does not have a specific editor configured for it.

Core Note

You can, in fact, remove a default editor by passing a null editor reference to setDefaultEditor.If you remove the default editor for object, cells containing data types that would otherwise select the object editor will no longer be editable. This course of action is not recommended because there are clearer ways to arrange for a cell to be read-only, as you have already seen.



Similarly, you can set or get an editor for a specific column using the following TableColumn methods:

 public void setCellEditor (TableCellEditor editor); public TableCellEditor getCellEditor (); 

In the example shown in Listing 7-1, no column editors were configured so clicking in either of the editable columns caused a default editor to be invoked. The table model's getColumnClass method shown in Listing 7-1 returns java.lang. Double for both of these columns (because the data held in the first row of the table is always of type Double). Because there is no default editor for Double, the editor for its superclass, java.lang.Number, is used instead. This means that the editing of currency values is performed using a right-justified JTextField, as you can verify by running the last example again.

The TableCellEditor and CellEditor Interfaces

All the methods that configure editors or obtain references to them deal with the type TableCellEditor, which is an interface that must be implemented by any class used as a table editor. TableCellEditor is derived from a more primitive interface called CellEditor that contains methods that are common to table editors and to the cell editors for trees (which implement the TreeCellEditor interface). This is how the CellEditor and TableCellEditor interfaces are defined:

 public interface CellEditor {   public Object getCellEditorValue();   public boolean isCellEditable(EventObject evt);   public boolean shouldSelectCell(EventObject evt);   public boolean stopCellEditing();   public void cancelCellEditing();   public void addCellEditorListener(CellEditorListener 1);   public void removeCellEditorListener(CellEditorListener 1); } public interface TableCellEditor extends CellEditor {   Component getTableCellEditorComponent(JTable table,                              Object value,                              boolean isSelected,                              int row, int column); } 

The TableCellEditor interface on its own is very similar to the TableCellRenderer interface used by renderers. Fundamentally, an editor manages an editing component that is used in much the same way as the component returned to the table by a renderer. The editing component is obtained by calling the getTableCellEditorComponent method, which may choose to configure the editing component using the parameters that are supplied to it.

Core Note

Be careful to distinguish between the editor and the editing component The editor is an instance of whichever class is implementing the TableCellEditor interface and is the object with which the table deals directly. The editing component is the component returned by the editor's getTableCellEditorComponent method and that will be visible to the user (the JTextField, JCheckBox, JcomboBox, and so forth). Throughout this chapter, the distinction between these two is made by careful use of terminology.



You'll see exactly how the table uses the editor and editing component in the discussion of the mechanics of the editing process next, in which the methods of the CellEditor interface, which are used to control the editing process rather than to manipulate the editing component, will also be described.

The DefaultCellEditor Class

You've already seen that the table configures default editors that are implemented as check boxes and text fields and that all table editors must implement the TableCellEditor interface. You also know, however, that no Swing component implements TableCellEditor, which means that you can't directly install a JComponent as a table editor. Instead, the tables default editors are instances of the DefaultCellEditor class, which implements the TableCellEditor interface (and the similar TreeCellEditor interface). The job of the DefaultCellEditor class is to delegate control of the actual editing to a Swing component, while providing the common code that interacts with the table to start and end the editing process. DefaultCellEditor provides all the methods of the TableCellEditor interface and a few more that can be used to configure its exact behavior.

DefaultCellEditor has three constructors, each of which takes a different Swing component:

 public DefaultCellEditor (JTextField editor); public DefaultCellEditor (JComboBox editor); public DefaultCellEditor (JCheckBox editor); 

Each of these takes the component that you give it and arranges for that component to be returned when the getTableCellEditorComponent method is invoked by the table. If you are creating a custom editor by subclassing DefaultCellEditor, you can, if you wish, perform specific customization of these components before passing them to the DefaultCellEditor constructor. Indeed, the standard table editors are instances of DefaultCellEditor, with either a JCheckBox or a JTextField configured with left-or right-aligned text depending on which class of data is to be edited.

The rest of the methods provided by DefaultCellEditor, excluding those required to implement the TableCellEditor interface, are shown here:

 public Component getComponent(); public void setClickCountToStart(); public int getClickCountToStart(); protected void fireEditingStopped(); protected void fireEditingCanceled(); 

Because these methods are not in the TableCellEditor interface, none of them are used by the table. Instead, the first three methods are intended for use either when setting up the editor or, during the editing process, by software that is aware that it is dealing with a DefaultCellEditor. The most important of these methods is setClickCountToStart, which determines how many clicks are required before the editing component starts editing. In the case of a text field editor, for example, the user needs to double-click in a cell to activate the editing process a single click simply selects the cell (if cell selection is enabled). By contrast, the other two default editors are activated by a single mouse click. It is important to note that it is DefaultCellEditor that determines when the edit will start, using the mouse click count and the value set by setClickCountToStart, rather than the table, or the editing component.

The last two methods are used internally by DefaultCellEditor to generate the events that are required when the editing process is completed or is canceled. More will be said about all of these methods in the next section.

If you want to create a custom editor that uses a JTextField, JcomboBox, or JCheckBox as the basic editing component, the simplest way to do it is to subclass DefaultCellEditor. You'll see a simple example of this later in this chapter. However, if your editor needs to use a different Swing component, you'll soon notice some shortcomings in the implementation of DefaultCellEditor that make subclassing it in this case slightly artificial. There is an example that demonstrates the problems and looks at the options available for working around them in "Using Table Editors". One of these options is to avoid using DefaultCellEditor and instead create a new class that directly implements the TableCellEditor interface for itself. Keep in mind, therefore, when reading the rest of this chapter that while many of the editors that the table uses will be based on DefaultCellEditor (and all the default ones are), this will not always be the case. When the editor directly implements the TableCellEditor interface, you cannot assume that it also supplies the DefaultCellEditor methods listed above.

The Mechanics of the Editing Process

The editing process has three distinct phases:

  1. Detecting that the user wants to edit a cell and installing the correct editor.

  2. Editing the data in the cell.

  3. Completing the edit, updating the table's data model, and removing the editor.

In this section, you'll see exactly what happens in each of these phases and which pieces of the table are involved at each point.

Starting the Edit

You've seen that the first phase can be initiated by the appropriate number of mouse clicks (two for a text editor, one for a combo box or a check box) in an editable cell, but there are two additional ways to begin editing. The most obvious way, which you can try by running the EditableHighlightCurrencyTable example that was shown earlier, is to move the focus to the cell to be edited using the cursor keys or with a single click and then just start typing a new value into the cell. The table UI class registers listeners that detect mouse presses and key presses and react to either by checking whether an edit should be initiated as a result. The other way to start an edit is for an application program to call one of the JTable editCellAt methods:

 public boolean editCellAt (int row, int column); public boolean editCellAt (int row, int column, EventObject evt); 

The EventObject argument is used to allow the editCellAt method to decide whether an edit should be started based on some aspect of the event that caused it to be invoked. Application programs that want to programmatically start an edit will usually want to do so unconditionally and will therefore call the first variant of this method.

Beginning an Edit on Mouse Action

Let's first consider what happens when the user clicks somewhere in the table. When this happens, the mouse listener registered by the table UI class receives a MOUSE_PRESSED event. If the left mouse button is pressed, the listener uses the mouse coordinates to work out which cell has been clicked; right and middle button clicks are ignored and are usually used by application code to post pop-up menus. If the click occurred inside a cell (as opposed to in the inter-cell gaps), the mouse handler calls the JTable editCellAt method, passing it the row and column indices of the cell and the MouseEvent. This method decides whether the click should start an edit and, if so, returns true. If false is returned, the click does not cause editing to begin but it may instead cause the cell or its containing row and/or column to be selected, depending on the selection criteria in use.

So, how does the JTable editCellAt method determine whether a click should actually cause editing to start? First, it calls the JTable isCellEditable method using the row and column indices that it has been passed to find out whether the user has clicked in a cell that can be modified. This call simply maps the column index, which is in terms of the column order displayed on the screen, to the TableModel column index and then calls the TableModel isCellEditable method to find out whether the model considers the cell to be editable.

Assuming that the cell is inherently editable, the next step is to get the appropriate editor for the cell, following the procedure described earlier in "Selecting an Editor". If there is a suitable editor (which there always will be unless the default editor for java.lang.Object has been deliberately removed), the table then calls the editor's isCellEditable method. Don't confuse the CellEditor isCellEditable method with the isCellEditable method of TableModel, which determines editability based only on the nature of the data model and may use the cell's row and column index as part of the decision-making process. The CellEditor isCellEditable method is defined as follows:

 public void isCellEditable (EventObject evt); 

In other words, this method can base its decision only on the event that caused the table to consider editing the cell and on criteria specific to the editor; it does not have direct access to the table data, unless a reference was stored with the editor when it was created. The default editors (and all these created using DefaultCellEditor) allow the edit to start if the event they are passed is not a mouse event or if the number of mouse clicks is at least the number required for the type of component that the editor will use. The actual number of clicks required is configured when the editor is created using the setClickCountToStart method of DefaultCellEditor; as you know, by default this number defaults to two clicks for a text editor and one click for others. Editors that are not derived from DefaultCellEditor use their own criteria to determine whether to allow editing to begin, as you'll see later when we look at the implementation of a custom editor.

Core Note

For now, it is being assumed that editing is triggered by a mouse event. Later, you'll see how this process changes if editing is being started for other reasons.



When it is determined that both the TableModel and the editor agree that editing is going to performed, the editors getTableCellEditorComponent method is called to get a suitably configured editing component, and then the editing component is sized to match the size of the cell being edited and moved to that cell's location. The editor component is then added to the JTable, so becomes a child component of the table. This, of course, is different from renderers, which are used to draw table cells but are never added to the table.

When the edit is initiated from a mouse click (and only in this case), the last step in attaching the editor to the table is to call the CellEditor shouldSelectCell method, which also receives the initiating event as its argument. If this method returns true, the cell being edited is selected that is, it becomes the selected cell if the CTRL and SHIFT keys are not pressed, or is added to the selection in the usual way if either of these keys is pressed.

Core Warning

The editing sequence described here is slightly different from that used in Swing 1.0, where the shouldSelectCell method was always called when the editor was added to the table and it was convenient to use it as a common point at which to initialize the editor. This is no longer possible because this method is not invoked when the edit is started either from the keyboard or programmatically and, when it is invoked now, it happens much later in the sequence. The only point at which the editor can be initialized in the new editing sequence is in its isCellEditable method, which, unfortunately, does not receive any information about the row and column to which the cell is being assigned. If you need this information, you will have to defer its use to the invocation of the getTableCellEditorComponent method.



During the process of installing the editor, the table stores useful information that can be retrieved using the methods shown in Table 7-1.

Table 7-1. Editor-Related Information Available from JTable
Information Method
Row being edited public int getEditingRow ( );
Column being edited public int getEditingColumn ( )
Current editor public TableCellEditor getCellEditor ( )
Editor Component public Component getEditorComponent ( )
Is table edit active? Public boolean isEditing ()

Note that most of this information is only valid once editing has started and becomes invalid when editing completes. After the editor has done its job, the editor and editor component will be returned as null and the row and column values will both be -1. However, the isEditing method always returns the correct result true if there is an edit in progress, false if not.

Finally, the table registers itself with the editor as a CellEditorListener to receive notification when editing is completed. You'll see how this works (and the definition of the CellEditorListener interface) in "Ending the Edit".

Beginning an Edit Using the Keyboard

When editing is initiated by typing into an editable cell that has the focus, the same steps as just described are performed with some slight differences. In this case, the initial event will be KeyEvent, caught by a KeyListener registered on the JTable by its UI class. When starting an edit from a mouse click, the table UI class passes the mouse event to editCellAt. However, in response to a key click, the table UI class invokes the variant of editCellAt that takes only two arguments (the cell's row and column index). This causes a null event to be given to the three-argument variant of editCellAt. As a result, the TableCellEditor method isCellEditable receives a null event parameter, so it has no information at all on which to base a decision about whether to start editing, except the knowledge that editing was not initiated by a mouse click. The DefaultCellEditor implementations of this method returns true in this case, meaning that editing will be allowed to begin.

Core Note

This behavior is reasonable, because the editors created using DefaultcellEditor use the MouseEvent only to compare the mouse click count with the number of clicks need to start editing. When a mouse is not in use, the concept of clicks does not apply, so there is no need for DefaultcellEditor to inspect on event.



As noted earlier, when editing is initiated using a keystroke, the should-SelectCell method is not called. However, the editing information listed above is still set up and the table registers itself with the editor as a CellEditorListener.

Explicitly Starting an Edit from Application Code

The final case to consider is starting an edit from an application program. Usually, an application will invoke the two-argument form of editCellAt, which is the same as starting the edit from a keystroke. However, an application may also choose to supply an arbitrary event and call the three-argument editCellAt, in which case the action taken will depend on the event passed and the cell editor in use. Unless the application programmer is in complete control of the table editors in use, it is recommended that the two-argument editCellAt method is used to programmatically initiate an edit. This case is otherwise the same as starting an edit using the keyboard in other words, the shouldSelectCell method is not called. You'll see an example that programmatically starts an edit in "Tabbing Between Editable Cells".

Editing the Cell Data

Once the editor has been installed, the user interacts directly with it to edit the cell's data content. When the data has been modified, the new content will be written to the table model and the editing process terminates; the latter part of the editing mechanism will be discussed later.

The only interesting part of the editing phase is how the events that the editing component needs actually get to it. The events that are of most interest to editing components are keyboard and mouse events. Let's look first at keyboard events. As you know, keyboard events always go to whichever (single) component has the input focus. Usually, a table consists of only one component the JTable. Therefore, at any give time, the input focus will be on the JTable or elsewhere in the application. If you are driving the JTable using the keyboard, you would give the table the focus by clicking somewhere inside its boundary with the mouse (which the table reacts to by grabbing the focus using the JComponent requestFocus method), so all keyboard events go to the JTable.

When the table is being edited, however, there are two components to be considered the JTable and the editing component. If the editor is a text component, it needs to receive the user's keystrokes, so it would appear that the table should pass the focus to the editing component when it is installed. In fact, though, the JTable does not pass the focus to the editing component. As a result of this, keystrokes intended for the editing component will actually go to the JTable, not to the editing component, so you would not expect the user to be able to type anything into the text component, which is clearly not what happens.

However, this is not the end of the story. The table UI class registers itself as a KeyListener of the JTable and so receives notification of all keys pressed while the table has the focus. When editing is in progress, this listener takes all KEY_PRESSED events and performs special handling that works only if the editing component is a JTextField (or a class derived from JTextField). The result of this processing is that the key event will be redirected to the editor kit behind the text field, thus achieving the same effect as if the key press had been passed directly to the editor.

Keystrokes, therefore, get to the editing component only if it is a JTextField. Other editors, including the standard JComboBox and JCheckBox editors, do not receive keystrokes at all for these editors, only the initial keystroke that activates editing is processed and even then it is not passed to the editing component. This means that it is not always possible to drive an editing component to its full potential using the keyboard.

Core Note

At least this is the case at the time of writing. The situation may improve in the future.



Mouse events are, however, potentially a different matter. Mouse events are not controlled by where the input focus is directed instead, they usually go directly to whichever component the mouse is over, the exception being when the mouse is being dragged, in which case the MOUSE_DRAGGED and MOUSE_RELEASED events go to the component that received the MOUSE_PRESSED event at the start of the drag operation. Therefore, once the editor is installed, clicking it with the mouse will cause the event generated to go directly to the editing component, not to the JTable. There is, however, a subtlety involved here.

The mouse click that starts the edit generates a MOUSE_PRESSED event that is passed to the JTable. This event, and subsequent mouse events, need to be delivered to whichever editing component is finally installed in the table. To make this possible, the table UI class remembers the component that is under the mouse when editing starts and redirects all mouse events it receives to that component, until editing is completed. This is necessary because all events after the MOUSE_PRESSED and up to the matching MOUSE_RELEASED (including MOUSE_CLICKED and MOUSE_DRAGGED, if any) will go to the JTable, not the newly installed editing component. As a result of this special handling, if you highlight the content of the editor by dragging the mouse over it, the events that this generates are passed to the editing component. This means that editor components behave normally with respect to mouse events and you can do anything with these components when they are installed inside a table that you can do when they are used on their own.

Ending the Edit

Once editing has started, there are only two ways to terminate it:

  1. Performing some gesture that the editing component can interpret as marking the end of the editing operation.

  2. Clicking with the mouse somewhere inside the table but outside the editing component.

Let's first look at what happens when the conditions for ending the edit have been satisfied; later, we'll describe how these two conditions are detected.

No matter why the edit is being completed, the cleanup operation is begun by invoking the stopCellEditing method of the current cell editor; this method is part of the CellEditor interface, so it is implemented by every editor. This method is defined as follows:

 public boolean stopCellEditing (); 

The fact that this method returns a boolean means that it can, in theory, refuse to stop editing at any given time by returning false. If this happens, the table ignores whatever caused it to attempt to terminate the edit. The standard editors (all created using DefaultCellEditor) always return true from this method, which means that the editing operation will be terminated. The stopCellEditing method can do whatever it needs to do to clean up the editing component, but it must not lose the new value that the user selected for the cell. If this method is going to return true, it must also notify all CellEditorListeners that editing is complete. For editors derived from DefaultCellEditor, this obligation can be discharged by calling its fireEditingStopped method. Here is the definition of the CellEditorListener interface:

 public interface CellEditorListener                            extends java.util.EventListener {    public void editingStopped (ChangeEvent e);    public void editingCanceled (ChangeEvent e); } 

Any class can register as a CellEditorListener by implementing this interface and calling the addCellEditorListener method of CellEditor. Because all editors implement the CellEditor interface, they all support registration of CellEditorListeners. Editors derived from DefaultCellEditor inherit its addCellEditorListener and removeCellEditorListener methods and do not need to provide their own implementation.

Notice there appear to be two ways to report to a listener that editing has terminated; either the edit has stopped, or been canceled. The distinction between these two is what should happen to the table data model. When the editingStopped method is called, the edit has been ended cleanly and it is expected that the new value will be stored in the table data model. By contrast, if editingCanceled is called, any changes that the user made should be discarded. In fact, the default table editors never call the editingCanceled method, so there is no way to abandon a table edit. This interface is also used by JTree, which does call the editingCanceled method under some circumstances.

Core Note

If you implement a custom table editor, you can define a key sequence for that editor that would cancel the edit The appropriate way to implement this is just to call the editingCanceled method of all registered CellEditorListeners, which can be done using the cancelcellEditing method of DefaultcellEditor if your editor is derived from it



JTable implements the CellEditorListener interface and, as you saw in "Starting the Edit", registers itself to receive these events with the editor when it starts the edit. Therefore, its editingstopped method will be invoked. You'll note from the definition of the editingstopped method that its only argument is a ChangeEvent, which carries no information at all other than a reference to its source. The meaning of the source for these events is not formally defined in the Swing API. Although the editors derived from DefaultCellEditor all supply the editor as the source, JTable cannot rely on this when a custom editor is installed, so it uses its own getCellEditor method to retrieve the current editor. It then invokes the CellEditor getEditorValue method on the editor to get the new value of the table cell. The editor itself, of course, does not know what the new cell value is; to get this value, it extracts it from the actual editing component. In the case of a JTextField, for example, this involves calling the getText method. DefaultCellEditor has the appropriate code to get the new value for the three component types that it supports.

With the new value available, JTable uses the TableModel setValueAt method to update the table model. At this point, no validation of the data has been performed (unless a custom editor has performed some kind of validity checking the default editors do not do this). It is up to the TableModel to reject values that are not legal, according to its own criteria. As you saw earlier in the discussion of the code in Listing 7-1, there is no way for the TableModel to provide feedback when it receives an illegal value it just discards the data and leaves the cell unchanged.

The last step is to disconnect the editor from the table. This job is performed by the JTable removeEditor method, which does the following:

  1. Deregisters the table as a CellEditorListener of the editor.

  2. Removes the editing component from its parent container, the JTable.

  3. Returns the input focus to the JTable if the editing component had grabbed it.

  4. Schedules a repaint of the cell that has just been edited.

The repaint is limited to the area of the table occupied by the edited cell. Because all table painting is handled by renderers, this operation will actually be performed by the cell's usual renderer. While the table is editing, the editor component is responsible for drawing the content of the cell. It is possible that the renderer and the editor will display the same data in different ways, so there will be an obvious difference between the cell's appearance when it is editing. This is certainly the case for data that is edited by the standard text editors, which show a lined border when an edit is active. The default renderers for the same data types do not have a border.

Finally, having seen what happens when editing is complete, let's return to the two ways in which the table detects that the current edit should be stopped. The most obvious way is for the user to directly signal the fact as part of the editing process. If the cell editor is a JTextField, for example, the user can press RETURN to complete the edit. This will generate an ActionEvent, which must be caught and the cell editors stopCellEditing method called in response. The DefaultCellEditor implementation includes the code to register an ActionListener on the JTextField and calls stopCellEditing in its actionPerformed method. The same arrangement works if the editor is a JCheckBox: or a JComboBox: because these components also generate an ActionEvent when their state is changed.

The other way in which the user can signal the end of an edit is to click somewhere else in the table. This generates a mouse event that is caught by the table UI class. This event is actually treated in the same way as mouse events that start an edit in fact, the same code is used. This means that the tables editCellAt method will be called, this time to determine whether to start an edit in a different cell. The description of this method that you saw earlier omitted one important fact: before deciding whether a new edit should be started, it checks whether a cell is currently being edited (using the JTable isEditing method). If so, it is immediately stopped by calling the editor's stopCellEditing method directly. When this method returns, the current edit will have been stopped and the editor removed. Any value typed into the cell will have been saved in the table's data model (assuming it was valid).

Core Note

Actually, as you know, stopCeliEditing could return false, in which case the active edit continues.



There are actually several other ways to cause the current edit to be stopped, all of which do not save the current value in the table (but nevertheless do not invoke the editingCanceled method of CellEditorListeners). These are:

  • Adding a column to the table model.

  • Removing a column from the table model.

  • Changing the margin of a table column.

  • Moving a column.

Of these, only the last can be performed directly by the user. This is the only way for the user to abandon a table edit without changing the data in the table model and without having to retype the previous content.

 

 



Core Swing
Core Swing: Advanced Programming
ISBN: 0130832928
EAN: 2147483647
Year: 1999
Pages: 55
Authors: Kim Topley

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net