View Javadoc

1   /*
2    * Copyright (c) 2004-2005 by Michael Connor. All Rights Reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    *
7    *  o Redistributions of source code must retain the above copyright notice,
8    *    this list of conditions and the following disclaimer.
9    *
10   *  o Redistributions in binary form must reproduce the above copyright notice,
11   *    this list of conditions and the following disclaimer in the documentation
12   *    and/or other materials provided with the distribution.
13   *
14   *  o Neither the name of FormLayoutBuilder or Michael Connor nor the names of
15   *    its contributors may be used to endorse or promote products derived
16   *    from this software without specific prior written permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.mlc.swing.layout;
31  
32  import java.awt.BorderLayout;
33  import java.awt.Color;
34  import java.awt.Component;
35  import java.awt.Container;
36  import java.awt.Font;
37  import java.awt.Frame;
38  import java.awt.Insets;
39  import java.awt.Point;
40  import java.awt.Rectangle;
41  import java.awt.event.*;
42  import java.util.*;
43  
44  import javax.swing.*;
45  import javax.swing.border.*;
46  import javax.swing.event.*;
47  import javax.swing.table.DefaultTableCellRenderer;
48  
49  import com.jgoodies.forms.factories.Borders;
50  import com.jgoodies.forms.factories.DefaultComponentFactory;
51  import com.jgoodies.forms.layout.CellConstraints;
52  
53  /***
54   * This is the main panel that is used in LayoutFrame serving as the user
55   * interface for the builder. This is a pretty juicy file because there is a lot
56   * going on here. Better docs to come...
57   *
58   * @author Michael Connor mlconnor@yahoo.com
59   */
60  @SuppressWarnings("serial")
61  public class FormEditor extends JPanel
62  {
63    String[] verticalAlignmentList = { LayoutConstraintsManager.DEFAULT,
64        LayoutConstraintsManager.FILL, LayoutConstraintsManager.CENTER,
65        LayoutConstraintsManager.TOP, LayoutConstraintsManager.BOTTOM };
66  
67    String[] horizontalAlignmentList = { LayoutConstraintsManager.DEFAULT,
68        LayoutConstraintsManager.FILL, LayoutConstraintsManager.CENTER,
69        LayoutConstraintsManager.LEFT, LayoutConstraintsManager.RIGHT };
70  
71    ColSpanSpinnerModel colSpinnerModel = new ColSpanSpinnerModel();
72  
73    RowSpanSpinnerModel rowSpinnerModel = new RowSpanSpinnerModel();
74  
75    Action newComponentAction = new NewComponentAction();
76  
77    Action removeComponentAction = new RemoveComponentAction();
78  
79    Action insertRowBeforeAction = new InsertRowBeforeAction();
80  
81    Action insertRowAfterAction = new InsertRowAfterAction();
82  
83    Action deleteRowAction = new DeleteRowAction();
84  
85    Action insertColumnBeforeAction = new InsertColumnBeforeAction();
86  
87    Action insertColumnAfterAction = new InsertColumnAfterAction();
88  
89    Action deleteColumnAction = new DeleteColumnAction();
90  
91    JComboBox verticalAlignmentCombo = new JComboBox(verticalAlignmentList);
92  
93    JComboBox horizontalAlignmentCombo = new JComboBox(horizontalAlignmentList);
94  
95    JSpinner rowSpanSpinner = new JSpinner(rowSpinnerModel);
96  
97    JSpinner columnSpanSpinner = new JSpinner(colSpinnerModel);
98  
99    JLabel columnSpanLabel = new JLabel("Column Span");
100 
101   JLabel horizontalAlignmentLabel = new JLabel("Horizontal Alignment");
102 
103   JLabel rowSpanLabel = new JLabel("Row Span");
104 
105   JLabel verticalAlignmentLabel = new JLabel("Vertical Alignment");
106 
107   JPanel contentPanel = new JPanel();
108 
109   JPanel insetsPanel = new JPanel();
110 
111   SpinnerNumberModel rightInsetSpinnerModel = new SpinnerNumberModel(0, 0,
112       Integer.MAX_VALUE, 1);
113 
114   SpinnerNumberModel topInsetSpinnerModel = new SpinnerNumberModel(0, 0,
115       Integer.MAX_VALUE, 1);
116 
117   SpinnerNumberModel bottomInsetSpinnerModel = new SpinnerNumberModel(0, 0,
118       Integer.MAX_VALUE, 1);
119 
120   SpinnerNumberModel leftInsetSpinnerModel = new SpinnerNumberModel(0, 0,
121       Integer.MAX_VALUE, 1);
122 
123   JSpinner rightInsetSpinner = new JSpinner(rightInsetSpinnerModel);
124 
125   JSpinner bottomInsetSpinner = new JSpinner(bottomInsetSpinnerModel);
126 
127   JSpinner leftInsetSpinner = new JSpinner(leftInsetSpinnerModel);
128 
129   JSpinner topInsetSpinner = new JSpinner(topInsetSpinnerModel);
130 
131   GridTableModel tableModel = new GridTableModel();
132 
133   JLabel insetsLabel = new JLabel("Insets");
134 
135   JLabel componentsLabel = new JLabel("Components (Drag n Drop)");
136 
137   JLabel componentPaletteLabel = new JLabel("Palette (Drag n Drop)");
138 
139   ComponentPaletteListModel componentPaletteListModel = new ComponentPaletteListModel();
140 
141   // KBR JList componentPalette = new JList(componentPaletteListModel);
142   DndList componentPalette = new DndList(this, componentPaletteListModel);
143 
144   JScrollPane componentPaletteScrollPane = new JScrollPane(componentPalette);
145 
146   ComponentSelectionListModel componentSelectionListModel = new ComponentSelectionListModel();
147 
148   DndList componentList = new DndList(this, componentSelectionListModel);
149 
150   JScrollPane componentListScrollPane = new JScrollPane(componentList);
151 
152   ComponentListCellRenderer componentListCellRenderer = new ComponentListCellRenderer();
153 
154   Component constraintsSeparator = DefaultComponentFactory.getInstance()
155       .createSeparator("Component Constraints");
156 
157   Component positionsSeparator = DefaultComponentFactory.getInstance()
158       .createSeparator("Component Positions (Drag n Drop)");
159 
160   JPanel componentsPanel = new JPanel();
161 
162   JPanel propertiesPanel = new JPanel();
163 
164   JSplitPane componentsSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
165       componentsPanel, propertiesPanel);
166 
167   JTextField colSpecField = new JTextField();
168 
169   JTextField rowSpecField = new JTextField();
170 
171   Set<Component> newComponents = new HashSet<Component>();
172 
173   LayoutConstraintsManager layoutConstraintsManager;
174 
175   JToolBar toolbar = new JToolBar();
176 
177   JButton newComponentButton = new JButton(newComponentAction);
178 
179   JButton removeComponentButton = new JButton(removeComponentAction);
180 
181   JButton columnDeleteButton = new JButton(deleteColumnAction);
182 
183   JButton columnInsertAfterButton = new JButton(insertColumnAfterAction);
184 
185   JButton columnInsertBeforeButton = new JButton(insertColumnBeforeAction);
186 
187   JButton rowDeleteButton = new JButton(deleteRowAction);
188 
189   JButton rowInsertBeforeButton = new JButton(insertRowBeforeAction);
190 
191   JButton rowInsertAfterButton = new JButton(insertRowAfterAction);
192 
193   Container container;
194 
195   ContainerLayout containerLayout;
196 
197   LayoutFrame layoutFrame;
198   DnDTable table = null;
199   JScrollPane tableScrollPane = null;
200   JSplitPane constraintsSplitPane = null;
201 
202   Component topComponent = null;
203 
204   boolean suspendConstraintControlUpdates = false;
205 
206   void setContainer(Container container)
207   {
208     java.awt.LayoutManager layoutManager = container.getLayout();
209     if (!(layoutManager instanceof ContainerLayout))
210       throw new RuntimeException(
211           "Container layout must be of type ContainerLayout");
212     this.container = container;
213   }
214 
215   public FormEditor(LayoutFrame layoutFrame, ContainerLayout layout,
216       Container container)
217   {
218     super();
219 
220     this.layoutFrame = layoutFrame;
221     table = new DnDTable(layoutFrame, this);
222     tableScrollPane = new JScrollPane(table);
223     constraintsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
224         tableScrollPane, componentsSplitPane);
225 
226     setContainer(container);
227     containerLayout = layout;
228 
229     table.setBackground(java.awt.Color.white);
230     table.setSelectionBackground(new Color(220, 220, 255));
231     table.setSelectionForeground(Color.black);
232 
233     table.setDefaultRenderer(Object.class, new ConstraintTableCellRenderer());
234     table.setRowHeight(20);
235     table.setModel(tableModel);
236     table.setCellSelectionEnabled(true);
237     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
238 
239     // let's put the cursor in the table so the create component
240     // icon is enabled. at least then the user knows what to do...
241     if (tableModel.getRowCount() > 1 && tableModel.getColumnCount() > 1)
242     {
243       // KBR do NOT force visible so constraints row can still be seen
244       setSelectedCell(1, 1, false);
245     }
246 
247     componentList.setCellRenderer(componentListCellRenderer);
248 
249     // let's setup all of the usability stuff...
250     componentsLabel.setLabelFor(componentListScrollPane);
251     // componentsLabel.setDisplayedMnemonic(KeyEvent.VK_S);
252     verticalAlignmentLabel.setLabelFor(verticalAlignmentCombo);
253     verticalAlignmentLabel.setDisplayedMnemonic(KeyEvent.VK_V);
254     horizontalAlignmentLabel.setLabelFor(horizontalAlignmentCombo);
255     horizontalAlignmentLabel.setDisplayedMnemonic(KeyEvent.VK_H);
256     columnSpanLabel.setLabelFor(columnSpanSpinner);
257     columnSpanLabel.setDisplayedMnemonic(KeyEvent.VK_C);
258     rowSpanLabel.setLabelFor(rowSpanSpinner);
259     rowSpanLabel.setDisplayedMnemonic(KeyEvent.VK_R);
260 
261     columnInsertAfterButton.setToolTipText("Insert a column after this column");
262     columnInsertBeforeButton
263         .setToolTipText("Insert a column before this column");
264     columnDeleteButton.setToolTipText("Delete this column");
265     rowInsertBeforeButton.setToolTipText("Insert a row before this row");
266     rowInsertAfterButton.setToolTipText("Insert a row after this row");
267 
268     // let's setup the table toolbar
269     toolbar.add(newComponentButton);
270     toolbar.add(removeComponentButton);
271     toolbar.addSeparator();
272     toolbar.add(columnDeleteButton);
273     toolbar.add(columnInsertBeforeButton);
274     toolbar.add(columnInsertAfterButton);
275     toolbar.addSeparator();
276     toolbar.add(rowDeleteButton);
277     toolbar.add(rowInsertBeforeButton);
278     toolbar.add(rowInsertAfterButton);
279 
280     setFormComponent(null);
281 
282     layoutConstraintsManager = LayoutConstraintsManager
283         .getLayoutConstraintsManager(FormEditor.class.getResourceAsStream(
284             "editableLayoutConstraints.xml"));
285 
286     layoutConstraintsManager.setLayout("mainLayout", contentPanel);
287     layoutConstraintsManager.setLayout("insetsLayout", insetsPanel);
288     layoutConstraintsManager.setLayout("componentsLayout", componentsPanel);
289     layoutConstraintsManager.setLayout("propertiesLayout", propertiesPanel);
290 
291     insetsPanel.add(rightInsetSpinner, "rightInsetSpinner");
292     insetsPanel.add(leftInsetSpinner, "leftInsetSpinner");
293     insetsPanel.add(topInsetSpinner, "topInsetSpinner");
294     insetsPanel.add(bottomInsetSpinner, "bottomInsetSpinner");
295 
296     componentsPanel.add(componentListScrollPane, "componentListScrollPane");
297     componentsPanel.add(componentsLabel, "componentsLabel");
298     propertiesPanel.add(componentPaletteScrollPane,
299         "componentPaletteScrollPane");
300     componentPalette.setCellRenderer(new ComponentPaletteListRenderer());
301     propertiesPanel.add(componentPaletteLabel, "componentPaletteLabel");
302 
303     contentPanel.add(rowSpanLabel, "rowSpanLabel");
304     contentPanel.add(horizontalAlignmentCombo, "horizontalAlignmentCombo");
305     contentPanel.add(horizontalAlignmentLabel, "horizontalAlignmentLabel");
306     contentPanel.add(rowSpanSpinner, "rowSpanSpinner");
307     contentPanel.add(verticalAlignmentCombo, "verticalAlignmentCombo");
308     contentPanel.add(columnSpanLabel, "columnSpanLabel");
309     contentPanel.add(verticalAlignmentLabel, "verticalAlignmentLabel");
310     contentPanel.add(columnSpanSpinner, "columnSpanSpinner");
311     contentPanel.add(insetsPanel, "insetsPanel");
312     contentPanel.add(insetsLabel, "insetsLabel");
313     contentPanel.add(constraintsSeparator, "constraintsSeparator");
314     contentPanel.add(positionsSeparator, "positionsSeparator");
315     contentPanel.add(toolbar, "toolbar");
316     contentPanel.add(constraintsSplitPane, "constraintsSplitPane");
317 
318     constraintsSplitPane.setDividerLocation(605);
319 
320     contentPanel.setBorder(Borders.DIALOG_BORDER);
321 
322     setLayout(new BorderLayout());
323     add(contentPanel, BorderLayout.CENTER);
324 
325     setupListeners();
326   }
327 
328   private void setupListeners()
329   {
330 
331     verticalAlignmentCombo.addActionListener(new ActionListener()
332     {
333       public void actionPerformed(ActionEvent e)
334       {
335         Component component = table.getSelectedControl();
336         if (component != null)
337         {
338           CellConstraints cellConstraints = getComponentConstraints(component);
339           cellConstraints.vAlign = LayoutConstraintsManager
340               .getAlignment((String) verticalAlignmentCombo.getSelectedItem());
341           updateLayout(component);
342         }
343       }
344     });
345 
346     horizontalAlignmentCombo.addActionListener(new ActionListener()
347     {
348       public void actionPerformed(ActionEvent e)
349       {
350         Component component = table.getSelectedControl();
351         if (component != null)
352         {
353           CellConstraints cellConstraints = getComponentConstraints(component);
354           cellConstraints.hAlign = LayoutConstraintsManager
355               .getAlignment((String) horizontalAlignmentCombo.getSelectedItem());
356           updateLayout(component);
357         }
358       }
359     });
360 
361     topInsetSpinnerModel.addChangeListener(new ChangeListener()
362     {
363       public void stateChanged(ChangeEvent e)
364       {
365         if (!suspendConstraintControlUpdates)
366         {
367           Component component = table.getSelectedControl();
368           CellConstraints constraints = getComponentConstraints(component);
369           Insets insets = new Insets(topInsetSpinnerModel.getNumber()
370               .intValue(), constraints.insets.left, constraints.insets.bottom,
371               constraints.insets.right);
372           constraints.insets = insets;
373           updateLayout(component);
374         }
375       }
376     });
377 
378     leftInsetSpinnerModel.addChangeListener(new ChangeListener()
379     {
380       public void stateChanged(ChangeEvent e)
381       {
382         if (!suspendConstraintControlUpdates)
383         {
384           Component component = table.getSelectedControl();
385           CellConstraints constraints = getComponentConstraints(component);
386           Insets insets = new Insets(constraints.insets.top,
387               leftInsetSpinnerModel.getNumber().intValue(),
388               constraints.insets.bottom, constraints.insets.right);
389           constraints.insets = insets;
390           updateLayout(component);
391         }
392       }
393     });
394 
395     rightInsetSpinnerModel.addChangeListener(new ChangeListener()
396     {
397       public void stateChanged(ChangeEvent e)
398       {
399         if (!suspendConstraintControlUpdates)
400         {
401           Component component = table.getSelectedControl();
402           CellConstraints constraints = getComponentConstraints(component);
403           Insets insets = new Insets(constraints.insets.top,
404               constraints.insets.left, constraints.insets.bottom,
405               rightInsetSpinnerModel.getNumber().intValue());
406           constraints.insets = insets;
407           updateLayout(component);
408         }
409       }
410     });
411 
412     bottomInsetSpinnerModel.addChangeListener(new ChangeListener()
413     {
414       public void stateChanged(ChangeEvent e)
415       {
416         if (!suspendConstraintControlUpdates)
417         {
418           Component component = table.getSelectedControl();
419           CellConstraints constraints = getComponentConstraints(component);
420           Insets insets = new Insets(constraints.insets.top,
421               constraints.insets.left, bottomInsetSpinnerModel.getNumber()
422                   .intValue(), constraints.insets.right);
423           constraints.insets = insets;
424           updateLayout(component);
425         }
426       }
427     });
428 
429     table.addMouseListener(new MouseAdapter()
430         {
431           public void mouseClicked(MouseEvent e)
432           {
433             if (e.getClickCount() == 2)
434             {
435               Point p = e.getPoint();
436               int row = table.rowAtPoint(p);
437               int col = table.columnAtPoint(p);
438               // support double-click:
439               Component component = table.getSelectedControl();
440               if ( component == null)
441                 return;
442 
443               /* invoke componentDef editor on double-clicked control */
444               String name = getComponentName(component);
445               ComponentDef componentDef = containerLayout.getComponentDef(name);
446               if ( componentDef != null )
447               {
448                 editComponent(componentDef, component, new CellConstraints(col, row));
449               }
450             }
451           }
452           });
453     componentList.addListSelectionListener(new ListSelectionListener()
454     {
455       public void valueChanged(ListSelectionEvent e)
456       {
457         if (!e.getValueIsAdjusting())
458         {
459           /* set selected component as selected, which causes a
460            * scroll-to-visible in the table */
461           Component thisComponent = (Component) componentList.getSelectedValue();
462 
463           CellConstraints constraints = getComponentConstraints(thisComponent);
464           if (constraints == null)
465             throw new RuntimeException(
466                 "Unable to find constraints for component " + thisComponent
467                     + " in layout " + containerLayout.getName());
468           int col = constraints.gridX;
469           int row = constraints.gridY;
470 
471           table.changeSelection(row, col, false, false);
472           topComponent = thisComponent;
473         }
474       }
475     });
476 
477     componentList.addMouseListener(new MouseAdapter()
478     {
479       public void mouseClicked(MouseEvent e)
480       {
481         if (e.getClickCount() == 2)
482         {
483           int index = componentList.locationToIndex(e.getPoint());
484           if ( index == -1 )
485             return;
486 
487           // Get item
488           ListModel lm = ((DndList)e.getSource()).getModel();
489           Component thisComponent = (Component)lm.getElementAt(index);
490 
491           String name = getComponentName(thisComponent);
492           ComponentDef compDef = containerLayout.getComponentDef(name);
493           CellConstraints constraints = getComponentConstraints(thisComponent);
494           if (constraints == null)
495             throw new RuntimeException(
496                 "Unable to find constraints for component " + thisComponent
497                     + " in layout " + containerLayout.getName());
498           editComponent(compDef,thisComponent,constraints);
499         }
500       }
501     } );
502   }
503 
504   String getComponentName(Component control)
505   {
506     return containerLayout.getComponentName(control);
507   }
508 
509   CellConstraints getComponentConstraints(Component component)
510   {
511     return containerLayout.getComponentConstraints(component);
512   }
513 
514   private void specsChanged()
515   {
516     updateLayouts();
517 
518     // lets go down the tree
519     Component[] children = container.getComponents();
520     for (int index = 0; index < children.length; index++)
521     {
522       Component component = children[index];
523       if (component instanceof Container)
524         ((Container) component).doLayout();
525     }
526   }
527 
528   void updateLayouts()
529   {
530     container.validate();
531     container.doLayout();
532 
533     Container parent = container;
534 
535     while (parent != null)
536     {
537       parent.validate();
538       parent = parent.getParent();
539     }
540   }
541 
542   private void setSelectedCell(int columnIndex, int rowIndex,
543                                boolean forceVisible)
544   {
545     // we don't want to update the selection interval if nothing changed...
546     table.getSelectionModel().setSelectionInterval(rowIndex, rowIndex);
547     table.getColumnModel().getSelectionModel().setSelectionInterval(
548         columnIndex, columnIndex);
549 
550     if (forceVisible)
551     {
552       // let's make sure the cell is in the visible range...
553       JViewport viewport = (JViewport) table.getParent();
554       Rectangle rect = table.getCellRect(rowIndex, columnIndex, true);
555       Point pt = viewport.getViewPosition();
556       rect.setLocation(rect.x - pt.x, rect.y - pt.y);
557       viewport.scrollRectToVisible(rect);
558     }
559   }
560 
561   @SuppressWarnings("serial")
562   private class ComponentSelectionListModel extends
563       javax.swing.AbstractListModel
564   {
565 //    private String selectedName = null;
566 
567     List<Component> sortedComponents = new ArrayList<Component>();
568 
569     public ComponentSelectionListModel()
570     {
571       super();
572     }
573 
574 // Bug: when the user does the following:
575 // 1) drags a new control into place
576 // 2) drags a 2nd new control into place
577 // 3) deletes the 2nd control
578 // 4) drags a new 2nd control into place
579 // we get an array out of bounds exception in getElementAt. The
580 // delete probably failed to update our list.
581     public Object getElementAt(int index)
582     {
583       Component component = sortedComponents.get(index);
584       return component;
585     }
586 
587     public int getSize()
588     {
589       sortedComponents = new ArrayList<Component>();
590 
591       if (container != null)
592       {
593         Component[] containerComponents = container.getComponents();
594         for (int index = 0; index < containerComponents.length; index++)
595         {
596           Component insertComponent = containerComponents[index];
597           String insertComponentName = getComponentName(insertComponent);
598 
599           int insertIndex = 0;
600           while (insertIndex < sortedComponents.size()
601               && insertComponentName != null)
602           {
603             Component testComponent = sortedComponents.get(insertIndex);
604             String testName = getComponentName(testComponent);
605             if (testName != null)
606               testName = testName.toUpperCase();
607             if (insertComponentName.toUpperCase().compareTo(testName) <= 0)
608               break;
609             else
610               insertIndex++;
611           }
612           sortedComponents.add(insertIndex, insertComponent);
613         }
614       }
615 
616       return container != null ? container.getComponentCount() : 0;
617     }
618 
619     public void fireDelete()
620     {
621       super.fireContentsChanged(this, 0, Math.max(0,
622           container.getComponents().length - 1));
623     }
624 
625     public void fireInsert()
626     {
627       super.fireContentsChanged(this, 0, Math.max(0,
628           container.getComponents().length - 1));
629     }
630   }
631 
632   class ComponentListCellRenderer extends JLabel implements ListCellRenderer
633   {
634     private static final long serialVersionUID = 1L;
635     Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
636 
637     public ComponentListCellRenderer()
638     {
639       super();
640       setOpaque(true);
641     }
642 
643     public Component getListCellRendererComponent(JList list, Object value,
644         int index, boolean isSelected, boolean cellHasFocus)
645     {
646       Component component = (Component) value;
647       String name = getComponentName(component);
648 
649       setComponentOrientation(list.getComponentOrientation());
650       if (isSelected)
651       {
652         setBackground(list.getSelectionBackground());
653         setForeground(list.getSelectionForeground());
654       }
655       else
656       {
657         setBackground(list.getBackground());
658         setForeground(list.getForeground());
659       }
660 
661       setText(name != null ? name : "(Untitled)");
662 
663       setEnabled(list.isEnabled());
664       Font font = list.getFont();
665       setFont(font.deriveFont(component.isVisible() ? Font.PLAIN : Font.BOLD));
666       setBorder((cellHasFocus) ? UIManager
667           .getBorder("List.focusCellHighlightBorder") : noFocusBorder);
668 
669       return this;
670     }
671   }
672 
673   private void insertColumn(int column)
674   {
675     for (int index = 0; index < container.getComponentCount(); index++)
676     {
677       Component component = container.getComponent(index);
678       CellConstraints constraints = getComponentConstraints(component);
679       if (constraints.gridX > column)
680         constraints.gridX++;
681     }
682 
683     try
684     {
685       containerLayout.addColumnSpec(column, "pref");
686       tableModel.fireTableStructureChanged();
687       setSelectedCell(column + 1, 0, true);
688       specsChanged();
689     }
690     catch (IllegalArgumentException iae)
691     {
692       JOptionPane.showMessageDialog(FormEditor.this, iae.getMessage(),
693           "Invalid Layout", JOptionPane.ERROR_MESSAGE);
694     }
695   }
696 
697   private void insertRow(int rowIndex)
698   {
699     for (int index = 0; index < container.getComponentCount(); index++)
700     {
701       Component component = container.getComponent(index);
702       CellConstraints constraints = getComponentConstraints(component);
703       if (constraints.gridY > rowIndex)
704         constraints.gridY++;
705     }
706 
707     try
708     {
709       containerLayout.addRowSpec(rowIndex, "pref");
710       tableModel.fireTableStructureChanged();
711       setSelectedCell(0, rowIndex + 1, true);
712       specsChanged();
713     }
714     catch (IllegalArgumentException iae)
715     {
716       JOptionPane.showMessageDialog(FormEditor.this, iae.getMessage(),
717           "Invalid Layout", JOptionPane.ERROR_MESSAGE);
718     }
719   }
720 
721   Component formComponent = null;
722 
723   // this method will act as the controller for the buttons
724   // and the cell constraints form
725   public void setFormComponent(Component component)
726   {
727     // KBR with this, selecting header row then table body row, doesn't enable
728     // 'add'
729     // if ( component == formComponent )
730     // return;
731     formComponent = component;
732 
733     CellConstraints constraints = formComponent != null ?
734                  getComponentConstraints(formComponent) : null;
735 
736     suspendConstraintControlUpdates = true;
737 
738     if (formComponent != null)
739     {
740       rowSpinnerModel.setComponent(formComponent);
741       colSpinnerModel.setComponent(formComponent);
742       verticalAlignmentCombo.setSelectedItem(LayoutConstraintsManager
743           .getAlignment(constraints.vAlign));
744       horizontalAlignmentCombo.setSelectedItem(LayoutConstraintsManager
745           .getAlignment(constraints.hAlign));
746       topInsetSpinnerModel.setValue(new Integer(constraints.insets.top));
747       bottomInsetSpinnerModel.setValue(new Integer(constraints.insets.bottom));
748       rightInsetSpinnerModel.setValue(new Integer(constraints.insets.right));
749       leftInsetSpinnerModel.setValue(new Integer(constraints.insets.left));
750     }
751 
752     verticalAlignmentCombo.setEnabled(constraints != null);
753     horizontalAlignmentCombo.setEnabled(constraints != null);
754     rightInsetSpinner.setEnabled(constraints != null);
755     leftInsetSpinner.setEnabled(constraints != null);
756     topInsetSpinner.setEnabled(constraints != null);
757     bottomInsetSpinner.setEnabled(constraints != null);
758     rowSpanSpinner.setEnabled(constraints != null);
759     columnSpanSpinner.setEnabled(constraints != null);
760 
761     int col = table.getSelectedColumn();
762     int row = table.getSelectedRow();
763 
764     // Don't allow 'add' on top of existing component
765     newComponentAction.setEnabled(col > 0 && row > 0 && formComponent == null);
766     removeComponentAction.setEnabled(constraints != null);
767     columnDeleteButton.setEnabled(row == 0 && col > 0 && containerLayout.getColumnCount() > 1);
768     columnInsertAfterButton.setEnabled(col > -1);
769     columnInsertBeforeButton.setEnabled(col > 0);
770     rowDeleteButton.setEnabled(col == 0 && row > 0 && containerLayout.getRowCount() > 1);
771     rowInsertBeforeButton.setEnabled(row > 0);
772     rowInsertAfterButton.setEnabled(row > -1);
773 
774     suspendConstraintControlUpdates = false;
775   }
776 
777   public void updateLayout(Component component)
778   {
779     if (suspendConstraintControlUpdates)
780       return;
781 
782     CellConstraints constraints = getComponentConstraints(component);
783 
784     // we have to update the containerLayout which is the keeper of all
785     // constraints. if we didn't do this then we wouldn't notice any changes
786     // when we went to print everything out.
787     String name = getComponentName(component);
788     // i don't like this direct access thing. this should be changed...
789     containerLayout.setCellConstraints(name, constraints);
790     // updateForm();
791 
792     // be careful when modifying the next few lines of code. this
793     // is tricky to get right. this seems to work.
794     container.invalidate();
795     container.doLayout();
796 
797     // KBR list (for example) doesn't seem to re-layout properly on drag&drop
798     // without these
799     container.validate();
800     container.repaint();
801 
802     if (component instanceof Container)
803     {
804       Container cContainer = (Container) component;
805       cContainer.invalidate();
806       cContainer.doLayout();
807     }
808   }
809 
810   private class ColSpanSpinnerModel extends AbstractSpinnerModel
811   {
812     CellConstraints constraints;
813 
814     Component component;
815 
816     public ColSpanSpinnerModel()
817     {
818     }
819 
820     public void setComponent(Component component)
821     {
822       this.component = component;
823       if (component != null)
824       {
825         constraints = getComponentConstraints(component);
826         fireStateChanged();
827       }
828       else
829       {
830         constraints = null;
831       }
832     }
833 
834     public Object getNextValue()
835     {
836       if (constraints == null)
837         return null;
838       Integer next = constraints.gridX + constraints.gridWidth - 1 < containerLayout
839           .getColumnCount() ? new Integer(constraints.gridWidth + 1) : null;
840       return next;
841     }
842 
843     public Object getPreviousValue()
844     {
845       if (constraints == null)
846         return null;
847       else
848       {
849         Integer previous = constraints.gridWidth > 1 ? new Integer(
850             constraints.gridWidth - 1) : null;
851         return previous;
852       }
853     }
854 
855     public Object getValue()
856     {
857       if (constraints == null)
858         return "";
859       else
860         return new Integer(constraints.gridWidth);
861     }
862 
863     public void setValue(Object value)
864     {
865       if (constraints == null || value == null)
866         return;
867 
868 //      Number val = (Number) value;
869       constraints.gridWidth = ((Number) value).intValue();
870       super.fireStateChanged();
871       updateLayout(component);
872 
873       // firing table data changed messes up the
874       // selection so we'll get it and then restore it...
875       int col = table.getSelectedColumn();
876       int row = table.getSelectedRow();
877       tableModel.fireTableDataChanged();
878       setSelectedCell(col, row, true);
879     }
880   }
881 
882   private class RowSpanSpinnerModel extends AbstractSpinnerModel
883   {
884     CellConstraints constraints;
885 
886     Component component;
887 
888     public RowSpanSpinnerModel()
889     {
890     }
891 
892     public void setComponent(Component component)
893     {
894       this.component = component;
895       if (component != null)
896       {
897         constraints = getComponentConstraints(component);
898         fireStateChanged();
899       }
900       else
901       {
902         constraints = null;
903       }
904     }
905 
906     public Object getNextValue()
907     {
908       if (constraints == null)
909         return null;
910       else
911       {
912         Integer next = constraints.gridY + constraints.gridHeight - 1 < containerLayout
913             .getRowCount() ? new Integer(constraints.gridHeight + 1) : null;
914         return next;
915       }
916     }
917 
918     public Object getPreviousValue()
919     {
920       if (constraints == null)
921         return null;
922       else
923       {
924         Integer previous = constraints.gridHeight > 1 ? new Integer(
925             constraints.gridHeight - 1) : null;
926         return previous;
927       }
928     }
929 
930     public Object getValue()
931     {
932       if (constraints == null)
933         return "";
934       else
935         return new Integer(constraints.gridHeight);
936     }
937 
938     public void setValue(Object value)
939     {
940       if (constraints == null || value == null)
941         return;
942 //      Number val = (Number) value;
943       constraints.gridHeight = ((Number) value).intValue();
944       super.fireStateChanged();
945       updateLayout(component);
946 
947       // firing table data changed messes up the
948       // selection so we'll get it and then restore it...
949       int col = table.getSelectedColumn();
950       int row = table.getSelectedRow();
951       tableModel.fireTableDataChanged();
952       setSelectedCell(col, row, true);
953     }
954   }
955 
956   /***
957    * Returns true if the named component was created by hand in this session
958    */
959   public boolean isNewComponent(Component component)
960   {
961     return newComponents.contains(component);
962   }
963 
964   @SuppressWarnings("serial")
965   private class NewComponentDialog0 extends JDialog
966   {
967     JTextField nameField = new JTextField();
968 
969     JLabel nameLabel = new JLabel("Name");
970 
971     JLabel typeLabel = new JLabel("Type");
972 
973     JButton okButton = new JButton("OK");
974 
975     JButton cancelButton = new JButton("Cancel");
976 
977     JComboBox typeCombo = new JComboBox();
978 
979     PropertyTableModel propertyTableModel = new PropertyTableModel();
980 
981     JTable propertyTable = new JTable();
982 
983     JScrollPane propertyScrollPane = new JScrollPane(propertyTable);
984 
985     boolean wasSuccessful = false;
986 
987     Component component = null;
988 
989     Map<String, Object> controlProperties = new HashMap<String, Object>();
990 
991     public NewComponentDialog0(Frame owner) throws Exception
992     {
993       super(owner, "Add Component", true);
994 
995       okButton.setMnemonic(KeyEvent.VK_O);
996       cancelButton.setMnemonic(KeyEvent.VK_C);
997       nameLabel.setDisplayedMnemonic(KeyEvent.VK_N);
998       typeLabel.setDisplayedMnemonic(KeyEvent.VK_T);
999       nameLabel.setLabelFor(nameField);
1000       typeLabel.setLabelFor(typeCombo);
1001 
1002 //      FormLayout layout = new FormLayout("right:max(40dlu;pref), 3dlu, 130dlu",
1003 //          "");
1004       JPanel content = new JPanel();
1005       content.setBorder(Borders.DIALOG_BORDER);
1006       layoutConstraintsManager.setLayout("newComponentContent", content);
1007       getContentPane().setLayout(new BorderLayout());
1008       getContentPane().add(content, BorderLayout.CENTER);
1009 
1010       content.add(typeCombo, "typeCombo");
1011       content.add(nameField, "nameField");
1012       content.add(typeLabel, "typeLabel");
1013       content.add(nameLabel, "nameLabel");
1014       content.add(propertyScrollPane, "propertyScrollPane");
1015       content.add(com.jgoodies.forms.factories.ButtonBarFactory
1016           .buildRightAlignedBar(new JButton[] { okButton, cancelButton }),
1017           "buttonPanel");
1018 
1019       propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
1020       propertyTable.setModel(propertyTableModel);
1021 
1022       pack();
1023 
1024       typeCombo.addItem(new DefaultComponentBuilder(javax.swing.JButton.class,
1025           new String[] { "text" }));
1026       typeCombo.addItem(new DefaultComponentBuilder(
1027           javax.swing.JCheckBox.class, new String[] { "text" }));
1028       typeCombo
1029           .addItem(new DefaultComponentBuilder(javax.swing.JComboBox.class));
1030       typeCombo.addItem(new DefaultComponentBuilder(javax.swing.JLabel.class,
1031           new String[] { "text" }));
1032       typeCombo.addItem(new JListComponentBuilder());
1033       typeCombo.addItem(new DefaultComponentBuilder(javax.swing.JPanel.class));
1034       typeCombo.addItem(new DefaultComponentBuilder(
1035           javax.swing.JPasswordField.class));
1036       typeCombo.addItem(new DefaultComponentBuilder(
1037           javax.swing.JRadioButton.class, new String[] { "text" }));
1038       typeCombo.addItem(new DefaultComponentBuilder(
1039           javax.swing.JScrollPane.class));
1040       typeCombo
1041           .addItem(new DefaultComponentBuilder(javax.swing.JSpinner.class));
1042       typeCombo.addItem(new JTableComponentBuilder());
1043       typeCombo
1044           .addItem(new DefaultComponentBuilder(javax.swing.JTextArea.class));
1045       typeCombo.addItem(new DefaultComponentBuilder(
1046           javax.swing.JTextField.class));
1047       typeCombo.addItem(new JToolBarComponentBuilder());
1048       typeCombo.addItem(new JTreeComponentBuilder());
1049       typeCombo.addItem(new SeparatorComponentBuilder());
1050       typeCombo.addItem(new ButtonBarComponentBuilder());
1051 
1052       typeCombo.addActionListener(new ActionListener()
1053       {
1054         public void actionPerformed(ActionEvent e)
1055         {
1056           controlProperties = new HashMap<String, Object>();
1057           propertyTableModel.fireTableDataChanged();
1058         }
1059       });
1060 
1061       okButton.addActionListener(new ActionListener()
1062       {
1063         public void actionPerformed(ActionEvent e)
1064         {
1065 
1066           try
1067           {
1068             if (nameField.getText().trim().length() == 0)
1069               throw new Exception("The name field is required");
1070 
1071 //            int currentCol = propertyTable.getSelectedColumn();
1072 //            int currentRow = propertyTable.getSelectedRow();
1073 
1074             ComponentBuilder builder = (ComponentBuilder) typeCombo
1075                 .getSelectedItem();
1076             component = builder.getInstance(controlProperties);
1077 
1078             wasSuccessful = true;
1079             dispose();
1080 
1081           }
1082           catch (Exception exception)
1083           {
1084             exception.printStackTrace();
1085             wasSuccessful = false;
1086             JOptionPane.showMessageDialog(null, exception.getMessage(),
1087                 "Error Creating Component", JOptionPane.ERROR_MESSAGE);
1088           }
1089         }
1090       });
1091 
1092       cancelButton.addActionListener(new ActionListener()
1093       {
1094         public void actionPerformed(ActionEvent e)
1095         {
1096           wasSuccessful = false;
1097           dispose();
1098         }
1099       });
1100     }
1101 
1102     public boolean wasSuccessful()
1103     {
1104       return wasSuccessful;
1105     }
1106 
1107     public String getComponentName()
1108     {
1109       return nameField.getText();
1110     }
1111 
1112     public String getComponentDeclaration()
1113     {
1114       ComponentBuilder builder = (ComponentBuilder) typeCombo.getSelectedItem();
1115       return builder.getDeclaration(getComponentName(), controlProperties);
1116     }
1117 
1118     public boolean isUsingLayoutComponent()
1119     {
1120       ComponentBuilder builder = (ComponentBuilder) typeCombo.getSelectedItem();
1121       return builder.isComponentALayoutContainer();
1122     }
1123 
1124     public Component getComponent()
1125     {
1126       return component;
1127     }
1128 
1129     public ComponentDef getComponentDef()
1130     {
1131         ComponentBuilder builder = (ComponentBuilder) typeCombo.getSelectedItem();
1132         return builder.getComponentDef(getComponentName(), controlProperties);
1133     }
1134 
1135     private class PropertyTableModel extends
1136         javax.swing.table.AbstractTableModel
1137     {
1138       public int getColumnCount()
1139       {
1140         return 2;
1141       }
1142 
1143       public int getRowCount()
1144       {
1145         ComponentBuilder builder = (ComponentBuilder) typeCombo
1146             .getSelectedItem();
1147         return builder != null ? builder.getProperties().size() : 0;
1148       }
1149 
1150       public boolean isCellEditable(int row, int col)
1151       {
1152         return col == 1;
1153       }
1154 
1155       public String getColumnName(int col)
1156       {
1157         return col == 0 ? "Property" : "Value";
1158       }
1159 
1160       public void setValueAt(Object aValue, int row, int col)
1161       {
1162         ComponentBuilder builder = (ComponentBuilder) typeCombo
1163             .getSelectedItem();
1164         List<BeanProperty> properties = builder.getProperties();
1165         BeanProperty property = properties.get(row);
1166         controlProperties.put(property.getName(), aValue);
1167       }
1168 
1169       public Object getValueAt(int rowIndex, int columnIndex)
1170       {
1171         ComponentBuilder builder = (ComponentBuilder) typeCombo
1172             .getSelectedItem();
1173         List<BeanProperty> properties = builder.getProperties();
1174         BeanProperty property = properties.get(rowIndex);
1175 
1176         return columnIndex == 0 ? property.getName() : controlProperties
1177             .get(property.getName());
1178       }
1179 
1180       public Component getComponent() throws Exception
1181       {
1182         ComponentBuilder builder = (ComponentBuilder) typeCombo
1183             .getSelectedItem();
1184         Component instance = builder.getInstance(controlProperties);
1185         return instance;
1186       }
1187     }
1188   }
1189 
1190   @SuppressWarnings("serial")
1191   private class ConstraintTableCellRenderer extends DefaultTableCellRenderer
1192   {
1193     public Component getTableCellRendererComponent(JTable table, Object value,
1194         boolean isSelected, boolean hasFocus, int row, int column)
1195     {
1196       String stringValue = null;
1197       if (value != null)
1198       {
1199         if (value instanceof Component)
1200         {
1201           String name = getComponentName((Component) value);
1202           stringValue = name == null ? "(Untitled)" : name;
1203         }
1204         else
1205         {
1206           // in this case it's a row or col header
1207           stringValue = (String) value;
1208         }
1209       }
1210 
1211       return super.getTableCellRendererComponent(table, stringValue,
1212           isSelected, hasFocus, row, column);
1213     }
1214   }
1215 
1216   @SuppressWarnings("serial")
1217   private class GridTableModel extends javax.swing.table.AbstractTableModel
1218   {
1219 
1220     public int getColumnCount()
1221     {
1222       return containerLayout != null ? containerLayout.getColumnCount() + 1 : 1;
1223     }
1224 
1225     public int getRowCount()
1226     {
1227       return containerLayout != null ? containerLayout.getRowCount() + 1 : 1;
1228     }
1229 
1230     public boolean isCellEditable(int row, int col)
1231     {
1232       return (row == 0 || col == 0) && !(row == 0 && col == 0);
1233     }
1234 
1235     public String getColumnName(int col)
1236     {
1237       return col == 0 ? "*" : "" + col;
1238     }
1239 
1240     public void setValueAt(Object aValue, int row, int col)
1241     {
1242       String value = (String) aValue;
1243       if (row == 0) // a column was changed
1244       {
1245         try
1246         {
1247           containerLayout.setColumnSpec(col - 1, value);
1248           specsChanged();
1249         }
1250         catch (IllegalArgumentException iae)
1251         {
1252           JOptionPane.showMessageDialog(FormEditor.this, iae.getMessage(),
1253               "Invalid Layout", JOptionPane.ERROR_MESSAGE);
1254         }
1255       }
1256       else if (col == 0)
1257       {
1258         try
1259         {
1260           containerLayout.setRowSpec(row - 1, value);
1261           specsChanged();
1262         }
1263         catch (Exception e)
1264         {
1265           JOptionPane.showMessageDialog(null, e.getMessage(),
1266               "Invalid row specification", JOptionPane.ERROR_MESSAGE);
1267         }
1268       }
1269     }
1270 
1271     public Object getValueAt(int rowIndex, int columnIndex)
1272     {
1273       if (rowIndex == 0 && columnIndex == 0)
1274         return null;
1275       if (rowIndex == 0)
1276         return containerLayout.getColumnSpec(columnIndex - 1);
1277       if (columnIndex == 0)
1278         return containerLayout.getRowSpec(rowIndex - 1);
1279 
1280       Component component = null;
1281 
1282       for (int index = 0; index < container.getComponentCount(); index++)
1283       {
1284         Component thisComponent = container.getComponent(index);
1285         // we don't want to show invisible components. we
1286         // have decided to make components that are added without
1287         // constraints invisible until they are dropped onto the form.
1288         // this is our way of hiding them.
1289         if (thisComponent.isVisible())
1290         {
1291           CellConstraints constraints = getComponentConstraints(thisComponent);
1292           if (constraints == null)
1293             throw new RuntimeException(
1294                 "Unable to find constraints for component " + thisComponent
1295                     + " in layout " + containerLayout.getName());
1296           if (columnIndex >= constraints.gridX
1297               && columnIndex < constraints.gridX + constraints.gridWidth
1298               && rowIndex >= constraints.gridY
1299               && rowIndex < constraints.gridY + constraints.gridHeight)
1300           {
1301             component = thisComponent;
1302             if (component == topComponent)
1303               break;
1304           }
1305         }
1306       }
1307 
1308       return component;
1309     }
1310   }
1311 
1312   @SuppressWarnings("serial")
1313   private class RemoveComponentAction extends AbstractAction
1314   {
1315     public RemoveComponentAction()
1316     {
1317       super();
1318       putValue(Action.SHORT_DESCRIPTION, "Remove the component (Alt+D)");
1319       putValue(Action.LONG_DESCRIPTION, "Remove the component (Alt+D)");
1320       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1321           .getResource("Remove24.gif")));
1322       putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_D,
1323           KeyEvent.CTRL_MASK));
1324     }
1325 
1326     public void actionPerformed(ActionEvent e)
1327     {
1328       Component selectedControl = table.getSelectedControl();
1329       String controlName = getComponentName(selectedControl);
1330       container.remove(selectedControl);
1331       tableModel.fireTableDataChanged();
1332 
1333       if (selectedControl instanceof Container
1334           && layoutFrame.hasContainer(controlName))
1335       {
1336         layoutFrame.removeContainer(controlName);
1337       }
1338       container.doLayout();
1339       container.repaint();
1340       componentSelectionListModel.fireDelete();
1341 
1342       setFormComponent(null);
1343       table.requestFocus();
1344     }
1345   }
1346 
1347   @SuppressWarnings("serial")
1348   private class NewComponentAction extends AbstractAction
1349   {
1350     public NewComponentAction()
1351     {
1352       super();
1353       putValue(Action.SHORT_DESCRIPTION, "Create a new component (Alt+N)");
1354       putValue(Action.LONG_DESCRIPTION, "Create a new component (Alt+N)");
1355       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1356           .getResource("New24.gif")));
1357       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
1358     }
1359 
1360     public void actionPerformed(ActionEvent e)
1361     {
1362       Frame frame = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
1363           FormEditor.this);
1364 
1365       NewComponentDialog0 newComponentDialog = null;
1366       int columnIndex = table.getSelectedColumn();
1367       int rowIndex = table.getSelectedRow();
1368 
1369       try
1370       {
1371         newComponentDialog = new NewComponentDialog0(frame);
1372         newComponentDialog.setVisible(true);
1373       }
1374       catch (Exception ex)
1375       {
1376         ex.printStackTrace();
1377       }
1378 
1379       if (newComponentDialog.wasSuccessful())
1380       {
1381         String controlName = newComponentDialog.getComponentName();
1382         Component newControl = newComponentDialog.getComponent();
1383         ComponentDef newCD = newComponentDialog.getComponentDef();
1384 
1385         if (containerLayout.getCellConstraints(controlName) != null)
1386         {
1387           JOptionPane.showMessageDialog(FormEditor.this, "A component named '"
1388               + controlName + "' already exists", "Error",
1389               JOptionPane.ERROR_MESSAGE);
1390         }
1391         else
1392         {
1393           // the best way to add this control is to setup the constraints
1394           // in the map of name->constraints and then add it to the container.
1395           // this layout manager will intercept it, find the constraints and
1396           // then
1397           // set it up properly in the table and assign the maps.
1398           CellConstraints newConstraints = new CellConstraints(columnIndex,
1399               rowIndex);
1400           if ( newCD != null )
1401               containerLayout.addComponent(controlName, newCD, newConstraints);
1402           containerLayout.getCellConstraints().put(controlName, newConstraints);
1403           container.add(newControl, controlName);
1404 
1405           // we need to keep track of the new components added so we can present
1406           // some code to the user to add back into their module.
1407           // newComponents.put(controlName,
1408           // newComponentDialog.getComponentDeclaration());
1409 
1410           // if it's a panel, let's add it to the LayoutFrame so it can be
1411           // manipulated too...
1412           if (newComponentDialog.isUsingLayoutComponent())
1413           {
1414             Container newContainer = (Container) newControl;
1415             layoutFrame.addContainer(controlName, newContainer);
1416           }
1417 
1418           componentSelectionListModel.fireInsert();
1419 
1420           table.changeSelection(newConstraints.gridY, newConstraints.gridX,
1421               false, false);
1422           updateLayout(newControl); // relayout preview
1423           updateLayouts();          // relayout all panels
1424 
1425           newComponents.add(newControl); // we're not generating code unless this happens
1426         }
1427       }
1428 
1429       table.requestFocus();
1430     }
1431   }
1432 
1433   @SuppressWarnings("serial")
1434   private class InsertRowBeforeAction extends AbstractAction
1435   {
1436     public InsertRowBeforeAction()
1437     {
1438       super();
1439       putValue(Action.SHORT_DESCRIPTION,
1440           "Inserts a row before the selected row");
1441       putValue(Action.LONG_DESCRIPTION, "Inserts a row before the selected row");
1442       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1443           .getResource("RowInsertBefore24.gif")));
1444       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_I));
1445     }
1446 
1447     public void actionPerformed(ActionEvent e)
1448     {
1449       int row = table.getSelectedRow();
1450       insertRow(row - 1);
1451       table.requestFocus();
1452     }
1453   }
1454 
1455   @SuppressWarnings("serial")
1456   private class InsertRowAfterAction extends AbstractAction
1457   {
1458     public InsertRowAfterAction()
1459     {
1460       super();
1461       putValue(Action.SHORT_DESCRIPTION, "Inserts a row after the selected row");
1462       putValue(Action.LONG_DESCRIPTION, "Inserts a row after the selected row");
1463       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1464           .getResource("RowInsertAfter24.gif")));
1465       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
1466     }
1467 
1468     public void actionPerformed(ActionEvent e)
1469     {
1470       int row = table.getSelectedRow();
1471       insertRow(row);
1472       table.requestFocus();
1473     }
1474   }
1475 
1476   @SuppressWarnings("serial")
1477   private class InsertColumnBeforeAction extends AbstractAction
1478   {
1479     public InsertColumnBeforeAction()
1480     {
1481       super();
1482       putValue(Action.SHORT_DESCRIPTION,
1483           "Inserts a column before the selected column");
1484       putValue(Action.LONG_DESCRIPTION,
1485           "Inserts a column before the selected column");
1486       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1487           .getResource("ColumnInsertBefore24.gif")));
1488       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_K));
1489     }
1490 
1491     public void actionPerformed(ActionEvent e)
1492     {
1493       int column = table.getSelectedColumn();
1494       insertColumn(column - 1);
1495       table.requestFocus();
1496     }
1497   }
1498 
1499   @SuppressWarnings("serial")
1500   private class InsertColumnAfterAction extends AbstractAction
1501   {
1502     public InsertColumnAfterAction()
1503     {
1504       super();
1505       putValue(Action.SHORT_DESCRIPTION,
1506           "Inserts a column after the selected column");
1507       putValue(Action.LONG_DESCRIPTION,
1508           "Inserts a column after the selected column");
1509       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1510           .getResource("ColumnInsertAfter24.gif")));
1511       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_L));
1512     }
1513 
1514     public void actionPerformed(ActionEvent e)
1515     {
1516       int column = table.getSelectedColumn();
1517       insertColumn(column);
1518       table.requestFocus();
1519     }
1520   }
1521 
1522   @SuppressWarnings("serial")
1523   private class DeleteRowAction extends AbstractAction
1524   {
1525     public DeleteRowAction()
1526     {
1527       super();
1528       putValue(Action.SHORT_DESCRIPTION, "Deletes the selected row");
1529       putValue(Action.LONG_DESCRIPTION, "Deletes the selected row");
1530       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1531           .getResource("RowDelete24.gif")));
1532       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
1533     }
1534 
1535     public void actionPerformed(ActionEvent e)
1536     {
1537       int rowIndex = table.getSelectedRow();
1538 
1539       // move any components that are on the deleted row or
1540       // above it down one
1541       for (int index = 0; index < container.getComponentCount(); index++)
1542       {
1543         Component component = container.getComponent(index);
1544         CellConstraints constraints = getComponentConstraints(component);
1545 
1546         if (constraints.gridY >= rowIndex && constraints.gridY > 1)
1547         {
1548           constraints.gridY--;
1549         }
1550         else
1551         {
1552           // if the row deleted was within the span of the component and the
1553           // component
1554           // is bigger than one cell...
1555           if (constraints.gridY + constraints.gridHeight - 1 >= rowIndex
1556               && constraints.gridHeight > 1)
1557             constraints.gridHeight--;
1558         }
1559       }
1560 
1561       containerLayout.removeRowSpec(rowIndex - 1);
1562 
1563       tableModel.fireTableRowsDeleted(rowIndex, rowIndex);
1564       table.changeSelection(Math.min(rowIndex, containerLayout.getRowCount()),
1565           0, false, false);
1566       specsChanged();
1567       table.requestFocus();
1568     }
1569   }
1570 
1571   @SuppressWarnings("serial")
1572   private class DeleteColumnAction extends AbstractAction
1573   {
1574     public DeleteColumnAction()
1575     {
1576       super();
1577       putValue(Action.SHORT_DESCRIPTION, "Deletes the selected column");
1578       putValue(Action.LONG_DESCRIPTION, "Deletes the selected column");
1579       putValue(Action.SMALL_ICON, new ImageIcon(FormEditor.class
1580           .getResource("ColumnDelete24.gif")));
1581       putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
1582     }
1583 
1584     public void actionPerformed(ActionEvent e)
1585     {
1586       int columnIndex = table.getSelectedColumn();
1587 
1588       for (int index = 0; index < container.getComponentCount(); index++)
1589       {
1590         Component component = container.getComponent(index);
1591         CellConstraints constraints = getComponentConstraints(component);
1592 
1593         if (constraints.gridX >= columnIndex && constraints.gridX > 1)
1594           constraints.gridX--;
1595         else
1596         {
1597           // if the col deleted was within the span of the component and the
1598           // component
1599           // is bigger than one cell...
1600           if (constraints.gridX + constraints.gridWidth - 1 >= columnIndex
1601               && constraints.gridWidth > 1)
1602             constraints.gridWidth--;
1603         }
1604       }
1605 
1606       containerLayout.removeColumnSpec(columnIndex - 1);
1607       tableModel.fireTableStructureChanged();
1608       table.changeSelection(0, Math.min(columnIndex, containerLayout
1609           .getColumnCount()), false, false);
1610       specsChanged();
1611       table.requestFocus();
1612     }
1613   }
1614 
1615   @SuppressWarnings("serial")
1616   class ComponentPaletteListModel extends AbstractListModel
1617   {
1618     List<ComponentDef> componentDefs = ComponentDef.createComponentDefs();
1619 
1620     public int getSize()
1621     {
1622       return componentDefs.size();
1623     }
1624 
1625     public Object getElementAt(int index)
1626     {
1627       return componentDefs.get(index);
1628     }
1629   }
1630 
1631   @SuppressWarnings("serial")
1632   class ComponentPaletteListRenderer extends JLabel implements ListCellRenderer
1633   {
1634     public ComponentPaletteListRenderer()
1635     {
1636       setOpaque(true);
1637     }
1638 
1639     /*
1640      * This method finds the image and text corresponding to the selected value
1641      * and returns the label, set up to display the text and image.
1642      */
1643     public Component getListCellRendererComponent(JList list, Object value,
1644         int index, boolean isSelected, boolean cellHasFocus)
1645     {
1646       ComponentDef componentDef = (ComponentDef) value;
1647       setIcon(componentDef.icon != null ? componentDef.icon : null);
1648       setText(componentDef.name);
1649       if (isSelected)
1650       {
1651         setBackground(list.getSelectionBackground());
1652         setForeground(list.getSelectionForeground());
1653       }
1654       else
1655       {
1656         setBackground(list.getBackground());
1657         setForeground(list.getForeground());
1658       }
1659       return this;
1660     }
1661   }
1662 
1663   public void updateList()
1664   {
1665     componentSelectionListModel.fireInsert();
1666   }
1667   public String uniqueName(String name, Component comp)
1668   {
1669     // insure the component name doesn't collide
1670 
1671     String newname = name;
1672     int suffix = 1;
1673     for (;;)
1674     {
1675       Component temp = containerLayout.getComponentByName(newname);
1676       // no such component, or found component was ourself, stop the search
1677       if ( temp == null || temp == comp )
1678         break; // exitloop
1679 
1680       newname = name + "_" + suffix;
1681       suffix++;
1682     }
1683     return newname;
1684   }
1685 
1686   public boolean editComponent(ComponentDef componentDef,
1687                                Component component,
1688                                CellConstraints cellConstraints)
1689   {
1690     if ( componentDef.isContainer )
1691       return false; //punt!
1692 
1693     // get original name for remove
1694     String name = getComponentName(component);
1695 
1696     NewComponentDialog dlg =
1697       NewComponentDialog.editDialog(layoutFrame,componentDef);
1698     if (!dlg.succeeded())
1699       return false;
1700 
1701     componentDef.name = uniqueName(dlg.getComponentName(),component);
1702     String newname = componentDef.name;
1703 
1704     Component newcomponent = dlg.getInstance();
1705     containerLayout.removeLayoutComponent(component);
1706     containerLayout.addComponent(newname, componentDef, cellConstraints);
1707 
1708     container.remove(component);
1709     container.add(newcomponent,newname);
1710 
1711     newComponents.remove(component);
1712     newComponents.add(newcomponent);
1713 
1714       if (componentDef.isContainer)
1715       {
1716 /*** @todo losing components INSIDE the container!! */
1717 //        layoutFrame.replaceContainer(name, newName, (Container) newcomponent);
1718         layoutFrame.removeContainer(name);
1719         layoutFrame.addContainer(newname, (Container)newcomponent);
1720       }
1721 
1722       updateLayout(newcomponent);
1723       updateList();
1724       updateLayouts();
1725       repaint();
1726     return true;
1727   }
1728 }