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.*;
33  import java.awt.datatransfer.DataFlavor;
34  import java.awt.datatransfer.Transferable;
35  import java.awt.dnd.*;
36  //import java.beans.BeanDescriptor;
37  //import java.beans.BeanInfo;
38  import javax.swing.*;
39  
40  //import beantest.util.BeanInfoFactory;
41  import com.jgoodies.forms.layout.CellConstraints;
42  
43  /***
44   * I'm creating a table subclass to make it easier to handle dragging and
45   * dropping
46   * http://www.hut.fi/~landerso/cccp/src/java/cccp/mappingtool/util/MTTable.java
47   */
48  @SuppressWarnings("serial") 
49  class DnDTable extends JTable implements DragSourceListener,
50      DragGestureListener, DropTargetListener, Autoscroll
51  {
52    protected DragSource fDragSource = null;
53    protected DropTarget fDropTarget = null;
54    protected Component dragComponent = null;
55  
56    final static int AUTOSCROLL_INSET_SIZE = 20;
57    final static int SCROLL_AMOUNT = 10;
58  
59    FormEditor parent;
60    LayoutFrame superparent;
61    
62    public DnDTable(LayoutFrame granddaddy, FormEditor daddy)
63    {
64      super();
65  
66      parent = daddy;
67      superparent = granddaddy;
68      
69      fDragSource = new DragSource();
70      fDropTarget = new DropTarget(this, this);
71      fDragSource.createDefaultDragGestureRecognizer(this,
72          DnDConstants.ACTION_MOVE, this);
73    }
74  
75    /***
76     * Drag and drop is kind of weird in that when you drag something, you often
77     * start in a cell that has a component in it but by the time the drag
78     * handling code get's the event the selection has moved into a cell that
79     * does have a component in it and the drag fails.
80     */
81    public void changeSelection(int rowIndex, int columnIndex, boolean toggle,
82        boolean extend)
83    {
84      super.changeSelection(rowIndex, columnIndex, toggle, extend);
85  
86      Component component = getSelectedControl();
87      parent.setFormComponent(component);
88  
89      if (component != null)
90      {
91        // let's update the list control so when they select
92        // something in the grid it becomes selected in the
93        // list and the property values susequently change.
94        parent.componentList.setSelectedValue(component, true);
95      }
96      else
97      {
98        ;
99      }
100   }
101 
102   /***
103    * Implements autoscrolling.
104    */
105   public Insets getAutoscrollInsets()
106   {
107     Rectangle visible = getVisibleRect();
108     Dimension size = getSize();
109     int top = 0, left = 0, bottom = 0, right = 0;
110     if (visible.y > 0)
111       top = visible.y + AUTOSCROLL_INSET_SIZE;
112     if (visible.x > 0)
113       left = visible.x + AUTOSCROLL_INSET_SIZE;
114     if (visible.y + visible.height < size.height)
115       bottom = size.height - visible.y - visible.height + AUTOSCROLL_INSET_SIZE;
116     if (visible.x + visible.width < size.width)
117       right = size.width - visible.x - visible.width + AUTOSCROLL_INSET_SIZE;
118     return new Insets(top, left, bottom, right);
119   }
120 
121   /***
122    * Implements autoscrolling.
123    */
124   public void autoscroll(Point cursorLocn)
125   {
126     Rectangle visible = getVisibleRect();
127     int x = 0, y = 0, width = 0, height = 0;
128     // Scroll left.
129     if (cursorLocn.x < visible.x + AUTOSCROLL_INSET_SIZE)
130     {
131       x = -SCROLL_AMOUNT;
132       width = SCROLL_AMOUNT;
133     }
134     // Scroll right.
135     else if (cursorLocn.x > visible.x + visible.width - AUTOSCROLL_INSET_SIZE)
136     {
137       x = visible.width + SCROLL_AMOUNT;
138       width = SCROLL_AMOUNT;
139     }
140     // Scroll up.
141     if (cursorLocn.y < visible.y + AUTOSCROLL_INSET_SIZE)
142     {
143       y = -SCROLL_AMOUNT;
144       height = SCROLL_AMOUNT;
145     }
146     // Scroll down.
147     else if (cursorLocn.y > visible.y + visible.height - AUTOSCROLL_INSET_SIZE)
148     {
149       y = visible.height + SCROLL_AMOUNT;
150       height = SCROLL_AMOUNT;
151     }
152     ((JComponent) getParent()).scrollRectToVisible(new Rectangle(x, y, width, height));
153   }
154 
155   public void dragEnter(DragSourceDragEvent event) {}
156   public void dragOver(DragSourceDragEvent event) 
157   {
158     DragSourceContext context = event.getDragSourceContext();
159     java.awt.Point location = event.getLocation();
160     Point org = this.getLocationOnScreen();
161     Point relLoc = new Point( location.x - org.x,
162                               location.y - org.y );
163     
164     int col = columnAtPoint(relLoc);
165     int row = rowAtPoint(relLoc);
166     Component component = getControlAt(col,row);
167     
168     if ( col < 1 || row < 1 || component != null )
169       context.setCursor(DragSource.DefaultMoveNoDrop);
170     else
171       context.setCursor(DragSource.DefaultMoveDrop);
172   }
173   public void dropActionChanged(DragSourceDragEvent event) {}
174   public void dragExit(DragSourceEvent event) {}
175   public void dragDropEnd(DragSourceDropEvent event) {}
176   public void dropActionChanged(DropTargetDragEvent event) {}
177   public void dragExit(DropTargetEvent event) {}
178 
179   public void dragGestureRecognized(DragGestureEvent event)
180   {
181     Point p = event.getDragOrigin();
182     int row = rowAtPoint(p);
183     int col = columnAtPoint(p);
184     Component component = getControlAt(col,row);
185     if (component != null)
186     {
187       event.startDrag(java.awt.dnd.DragSource.DefaultMoveDrop,
188           new TransferableWrapper(component), this);
189     }
190   }
191 
192   public void dragEnter(DropTargetDragEvent dropTargetDragEvent)
193   {
194     try
195     {
196       if (dropTargetDragEvent.isDataFlavorSupported(new DataFlavor(
197           DataFlavor.javaJVMLocalObjectMimeType)))
198         dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE);
199       else
200         dropTargetDragEvent.rejectDrag();
201     }
202     catch (ClassNotFoundException cnfe)
203     {
204       cnfe.printStackTrace();
205     }
206   }
207 
208   public void dragOver(java.awt.dnd.DropTargetDragEvent dropTargetDragEvent)
209   {
210     DropTargetContext context = dropTargetDragEvent.getDropTargetContext();
211     
212     try
213     {
214       java.awt.Point location = dropTargetDragEvent.getLocation();
215       int col = columnAtPoint(location);
216       int row = rowAtPoint(location);
217       
218       if (dropTargetDragEvent.isDataFlavorSupported(new DataFlavor(
219             DataFlavor.javaJVMLocalObjectMimeType)))
220         dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE);
221       else
222       {
223         dropTargetDragEvent.rejectDrag();
224       }
225     }
226     catch (ClassNotFoundException cnfe)
227     {
228       cnfe.printStackTrace();
229     }
230   }
231 
232   public void drop(java.awt.dnd.DropTargetDropEvent e)
233   {
234     CellConstraints componentConstraints = null;
235     Component component = null;
236 
237     try
238     {
239 
240       java.awt.Point location = e.getLocation();
241       int col = columnAtPoint(location);
242       int row = rowAtPoint(location);
243       Component existComp = getControlAt(col,row);
244 
245       if ( col < 1 || row < 1 || existComp != null )
246       {
247         // don't allow drop onto constraints row/col
248         e.rejectDrop();
249         e.getDropTargetContext().dropComplete(true);
250         return;
251       }
252 
253       DataFlavor javaObject = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
254       if ( !e.isDataFlavorSupported(javaObject) )
255       {
256         // flavor unsupported
257         e.rejectDrop();
258         return;
259       }
260       
261       {
262         e.acceptDrop(DnDConstants.ACTION_MOVE);
263         Transferable transferable = e.getTransferable();
264         Object dropObject = transferable.getTransferData(javaObject);
265 
266         if (dropObject instanceof ComponentDef)
267         {
268           ComponentDef componentDef = (ComponentDef) dropObject;
269 
270           NewComponentDialog dlg = NewComponentDialog.doDialog(superparent, componentDef);
271           if (dlg.succeeded())
272           {
273             String componentName = dlg.getComponentName();
274             componentDef = dlg.componentDef;
275 
276             // insure the component name doesn't collide
277             int suffix = 1;
278             while (parent.containerLayout.getComponentByName(componentName) != null)
279             {
280               componentName = dlg.getComponentName() + "_" + suffix;
281               suffix++;
282             }
283             componentDef.name = componentName; // store new name for later edit
284             
285             component = null;
286             try
287             {
288               component = dlg.getInstance();
289               componentConstraints = new CellConstraints(col, row);
290               
291               // Earlier attempts to use beaninfo wasn't working for JPanel,
292               // and did identify the JGoodies buttonbar as a container (which
293               // it is, but not for our purposes).
294               boolean isContainer = componentDef.isContainer;
295 
296               // the best way to add this control is to setup the constraints
297               // in the map of name->constraints and then add it to the
298               // container.
299               // this layout manager will intercept it, find the constraints
300               // and then set it up properly in the table and assign the maps.
301               parent.containerLayout.addComponent(componentName, componentDef, componentConstraints);
302               parent.container.add(component, componentName);
303               parent.newComponents.add(component);
304 
305               if (isContainer)
306                 superparent.addContainer(componentName, (Container) component);
307 
308               e.dropComplete(true);
309               parent.updateList();
310 
311               parent.updateLayout(component); // force preview relayout
312               parent.updateLayouts(); // the key for updating with new panel
313               changeSelection(componentConstraints.gridY,componentConstraints.gridX, false, false);
314               repaint();
315               return;
316 
317             }
318             catch (Throwable t)
319             {
320               t.printStackTrace();
321 //              JOptionPane
322 //                  .showMessageDialog(parent,
323 //                      "Unable to create new instance of "
324 //                          + componentClass.getName() + "(" + t.getMessage()
325 //                          + ")", "Error", JOptionPane.ERROR_MESSAGE);
326               e.dropComplete(false);
327               return;
328             }
329           }
330         }
331 
332         else if (dropObject instanceof Component)
333         {
334           component = (Component) dropObject;
335           componentConstraints = parent.getComponentConstraints(component);
336 
337           if (col > 0 && row > 0)
338           {
339             componentConstraints.gridX = col;
340             componentConstraints.gridY = row;
341             componentConstraints.gridWidth = Math.min(
342                 componentConstraints.gridWidth, parent.containerLayout
343                     .getColumnCount()
344                     - componentConstraints.gridX + 1);
345             componentConstraints.gridHeight = Math.min(
346                 componentConstraints.gridHeight, parent.containerLayout
347                     .getRowCount()
348                     - componentConstraints.gridY + 1);
349 
350             if (!component.isVisible())
351               component.setVisible(true);
352             parent.topComponent = component; // make sure this sorts to the top...
353 
354             e.dropComplete(true);
355 
356             // we either moved a component or added it to the
357             // container for the first time so either way we
358             // need to update the layout...
359             parent.updateLayout(component);
360             changeSelection(componentConstraints.gridY,componentConstraints.gridX, false, false);
361             // we repaint the table because it has changed. i would think that
362             // firing a table update event would be more appropriate but this seems
363             // to work just fine.
364             repaint();
365             // repaint the list because we change the component text from bold
366             // to plain once it's been added.
367             parent.updateList();
368 
369             return;
370           }
371         }
372         else
373         {
374           // someone dropped something unexpected on us...
375           e.dropComplete(false);
376           return;
377         }
378       }
379     }
380     catch (Exception exception)
381     {
382       exception.printStackTrace();
383       // flavor exception or ClassNotFoundException
384       e.rejectDrop();
385       return;
386     }
387   }
388 
389   public Component getControlAt(int col, int row)
390   {
391     Component component = (row == 0 || col == 0) ? null
392         : (Component) getModel().getValueAt(row, col);
393     return component;
394   }
395   public Component getSelectedControl()
396   {
397     int col = getSelectedColumn();
398     int row = getSelectedRow();
399     return getControlAt(col,row);
400   }
401 }