2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.modules.form.menu;
43
import java.awt.Color;
44
import java.awt.Component;
45
import java.awt.Cursor;
46
import java.awt.Dimension;
47
import java.awt.Insets;
48
import java.awt.Point;
49
import java.util.ArrayList;
50
import java.util.List;
51
import javax.swing.BorderFactory;
52
import javax.swing.JCheckBoxMenuItem;
53
import javax.swing.JComponent;
54
import javax.swing.JLayeredPane;
55
import javax.swing.JMenu;
56
import javax.swing.JMenuBar;
57
import javax.swing.JMenuItem;
58
import javax.swing.JRadioButtonMenuItem;
59
import javax.swing.JSeparator;
60
import javax.swing.SwingUtilities;
61
import org.netbeans.modules.form.FormEditor;
62
import org.netbeans.modules.form.FormModel;
63
import org.netbeans.modules.form.HandleLayer;
64
import org.netbeans.modules.form.MetaComponentCreator;
65
import org.netbeans.modules.form.RADComponent;
66
import org.netbeans.modules.form.RADVisualComponent;
67
import org.netbeans.modules.form.RADVisualContainer;
68
import org.netbeans.modules.form.layoutdesign.LayoutComponent;
69
import org.netbeans.modules.form.palette.PaletteItem;
70
import org.netbeans.modules.form.palette.PaletteUtils;
73
* DragOperation handles all drag operations whether they are drag and drop or pick and plop. It
74
* also deals with new components from the palette and rearranging existing menu items within the menu.
75
* It does *not* handle the actual adding and removing of components. Instead that is delegated back
76
* to the MenuEditLayer.
77
* @author joshua.marinacci@sun.com
80
private static final boolean DEBUG = false;
81
private MenuEditLayer menuEditLayer;
82
private JComponent dragComponent;
83
private boolean started = false;
84
private JComponent targetComponent;
85
private enum Op { PICK_AND_PLOP_FROM_PALETTE, INTER_MENU_DRAG, NO_MENUBAR };
86
private Op op = Op.PICK_AND_PLOP_FROM_PALETTE;
87
private JMenuItem payloadComponent;
88
private List<JMenuItem> payloadComponents;
89
private PaletteItem currentItem;
91
public boolean isPickAndPlop() {
92
return op == Op.PICK_AND_PLOP_FROM_PALETTE;
95
public DragOperation(MenuEditLayer menuEditLayer) {
96
this.menuEditLayer = menuEditLayer;
100
public JComponent getDragComponent() {
101
return dragComponent;
104
// start a drag from one menu item to another
105
void start(JMenuItem item, Point pt) {
106
op = Op.INTER_MENU_DRAG;
107
p("starting an inner menu drag for: " + item + " at " + pt);
111
//josh: intial support for dragging multiple items
112
//in the future we should use the payloadComponents variable
113
//for dragging components instead of the payloadComponent variable.
114
List<RADComponent> rads = menuEditLayer.getSelectedRADComponents();
116
payloadComponents = new ArrayList<JMenuItem>();
117
if(rads.size() > 1) {
118
for(RADComponent rad : rads) {
119
Object comp = menuEditLayer.formDesigner.getComponent(rad);
120
if(comp instanceof JMenuItem) {
121
payloadComponents.add((JMenuItem)comp);
128
payloadComponents.add(item);
131
dragComponent = (JMenuItem) createDragFeedbackComponent(item, null);
132
dragComponent.setSize(dragComponent.getPreferredSize());
133
dragComponent.setLocation(pt);
134
menuEditLayer.layers.add(dragComponent, JLayeredPane.DRAG_LAYER);
135
menuEditLayer.repaint();
136
payloadComponent = item;
139
void setTargetVisible(boolean vis) {
141
menuEditLayer.layers.remove(dragComponent);
143
menuEditLayer.layers.add(dragComponent, JLayeredPane.DRAG_LAYER);
145
menuEditLayer.repaint();
148
private JComponent createDragFeedbackComponent(JMenuItem item, Class type) {
149
// get the pre-created component for use as drag feedback
150
PaletteItem paletteItem = PaletteUtils.getSelectedItem();
151
if(paletteItem != null) {
152
MetaComponentCreator creator = menuEditLayer.formDesigner.getFormModel().getComponentCreator();
153
RADVisualComponent precreated = creator.precreateVisualComponent(
154
paletteItem.getComponentClassSource());
155
if(precreated != null) {
156
p("precreated: " + precreated.getBeanClass());
157
Object comp = precreated.getBeanInstance();
158
if(comp instanceof JComponent) {
159
JComponent jcomp = (JComponent) comp;
160
p("it's a jcomponent");
161
if(comp instanceof JMenuItem) {
162
JMenuItem mcomp = (JMenuItem) comp;
163
mcomp.setBorder(MenuEditLayer.DRAG_MENU_BORDER);
164
mcomp.setIcon(new MenuEditLayer.WrapperIcon());
165
mcomp.setMargin(new Insets(1,1,1,1));
166
mcomp.setBorderPainted(true);
168
if(comp instanceof JSeparator) {
169
p("it's a separator");
170
//jcomp.setBorder(BorderFactory.createLineBorder(new Color(0xFFA400), 1));//, thickness)MenuEditLayer.DRAG_SEPARATOR_BORDER);
171
//jcomp.setPreferredSize(new Dimension(80,5));
172
p("border = " + jcomp.getBorder());
179
JComponent dragComponent = null;
180
dragComponent = new JMenuItem();
182
if(item == null && type != null && JComponent.class.isAssignableFrom(type)) {
184
dragComponent = (JComponent)type.newInstance();
185
p("created a drag component here: " + dragComponent);
186
} catch (Exception ex) {
187
System.out.println("exception: " + ex.getMessage());
188
ex.printStackTrace();
191
if(item instanceof JMenu) {
192
dragComponent = new JMenu();
194
if(item instanceof JCheckBoxMenuItem) {
195
dragComponent = new JCheckBoxMenuItem();
196
((JCheckBoxMenuItem)dragComponent).setSelected(true);
198
if(item instanceof JRadioButtonMenuItem) {
199
dragComponent = new JRadioButtonMenuItem();
200
((JRadioButtonMenuItem)dragComponent).setSelected(true);
202
if(dragComponent instanceof JMenuItem) {
203
JMenuItem dragItem = (JMenuItem) dragComponent;
205
dragItem.setText(item.getText());
206
dragItem.setIcon(item.getIcon());
207
if(! (item instanceof JMenu)) {
208
if(!DropTargetLayer.isMetal()) {
209
dragItem.setAccelerator(item.getAccelerator());
213
dragItem.setText("a new menu item");
215
dragItem.setMargin(new Insets(1,1,1,1));
216
dragItem.setBorderPainted(true);
218
dragComponent.setBorder(MenuEditLayer.DRAG_MENU_BORDER);
219
return dragComponent;
223
// start a pick and plop from the palette operation
224
void start(PaletteItem item, Point pt) {
225
// clean up prev is necessary
226
if(dragComponent != null) {
227
menuEditLayer.layers.remove(dragComponent);
228
dragComponent = null;
231
if(!menuEditLayer.doesFormContainMenuBar()) {
232
//op = Op.NO_MENUBAR;
233
menuEditLayer.showMenubarWarning = true;
234
FormEditor.getAssistantModel(menuEditLayer.formDesigner.getFormModel()).setContext("missingMenubar"); // NOI18N
235
menuEditLayer.repaint();
238
op = Op.PICK_AND_PLOP_FROM_PALETTE;
239
p("starting drag op for : " + item.getComponentClassName() + " at " + pt);
241
dragComponent = createDragFeedbackComponent(null, item.getComponentClass());
242
dragComponent.setSize(dragComponent.getPreferredSize());
243
p("created drag component = " + dragComponent);
244
dragComponent.setLocation(pt);
245
menuEditLayer.layers.add(dragComponent, JLayeredPane.DRAG_LAYER);
246
menuEditLayer.repaint();
248
menuEditLayer.glassLayer.requestFocusInWindow();
251
void move(Point pt) {
252
if(dragComponent != null) {
253
// move the drag component
254
dragComponent.setLocation(pt);
257
// look at the rad component under the cursor before checking the popups
258
RADComponent rad = menuEditLayer.formDesigner.getHandleLayer().getMetaComponentAt(pt, HandleLayer.COMP_DEEPEST);
260
// if dragging a JMenu over an open spot in the menu bar
261
if(rad != null && JMenuBar.class.isAssignableFrom(rad.getBeanClass()) && JMenu.class.isAssignableFrom(dragComponent.getClass())) {
262
//p("over the menu bar");
263
menuEditLayer.dropTargetLayer.setDropTarget(rad, pt);
264
targetComponent = (JComponent) menuEditLayer.formDesigner.getComponent(rad);
267
// open any relevant top-level menus
268
if(rad != null && JMenu.class.isAssignableFrom(rad.getBeanClass())) {
269
//p("over a menu: " + rad);
270
targetComponent = (JComponent) menuEditLayer.formDesigner.getComponent(rad);
271
menuEditLayer.openMenu(rad, targetComponent);
272
if(JMenu.class.isAssignableFrom(dragComponent.getClass())) {
273
menuEditLayer.dropTargetLayer.setDropTarget(rad, pt);
275
menuEditLayer.dropTargetLayer.setDropTarget(rad, pt, DropTargetLayer.DropTargetType.INTO_SUBMENU);
280
//show any drop target markers
281
Component child = getDeepestComponentInPopups(pt);
284
if(child instanceof JMenuItem && child != dragComponent) {
285
targetComponent = (JComponent)child;
286
if(targetComponent != null) {
287
menuEditLayer.dropTargetLayer.setDropTarget(targetComponent, pt, DropTargetLayer.DropTargetType.INTER_MENU);
289
menuEditLayer.repaint();
292
if(child instanceof JMenu) {
293
Point pt2 = SwingUtilities.convertPoint(menuEditLayer.glassLayer, pt, child);
294
JMenu menu = (JMenu) child;
296
menu.setBorderPainted(true);
297
if(DropTargetLayer.isSubMenuRightEdge(point, menu)) {
298
menuEditLayer.dropTargetLayer.setDropTarget(menu, point, DropTargetLayer.DropTargetType.INTO_SUBMENU);
301
menuEditLayer.dropTargetLayer.setDropTarget(menu, pt, DropTargetLayer.DropTargetType.INTER_MENU);
303
menuEditLayer.showMenuPopup(menu);
307
menuEditLayer.dropTargetLayer.clearDropTarget();
311
p("DragOperation: dragComponent shouldn't be null when moving");
320
void end(Point pt, boolean clear) {
323
if(dragComponent == null) return;
324
p("ending an operation at: " + pt);
325
menuEditLayer.layers.remove(dragComponent);
326
menuEditLayer.dropTargetLayer.clearDropTarget();
330
case PICK_AND_PLOP_FROM_PALETTE: completePickAndPlopFromPalette(pt, clear); break;
331
case INTER_MENU_DRAG: completeInterMenuDrag(pt); break ;
332
case NO_MENUBAR: /* do nothing */ break;
335
menuEditLayer.glassLayer.requestFocusInWindow();
336
payloadComponent = null;
337
targetComponent = null;
338
menuEditLayer.repaint();
344
if(dragComponent != null) {
345
menuEditLayer.layers.remove(dragComponent);
346
menuEditLayer.repaint();
348
menuEditLayer.dropTargetLayer.clearDropTarget();
351
// only looks at JMenu and JMenubar RADComponents as well as anything in the popups
352
JComponent getDeepestComponent(Point pt) {
353
if(pt == null) return null;
354
RADComponent rad = menuEditLayer.formDesigner.getHandleLayer().getMetaComponentAt(pt, HandleLayer.COMP_DEEPEST);
355
if(rad != null && (JMenu.class.isAssignableFrom(rad.getBeanClass()) ||
356
JMenuBar.class.isAssignableFrom(rad.getBeanClass()))) {
357
return (JComponent) menuEditLayer.formDesigner.getComponent(rad);
359
return (JComponent) getDeepestComponentInPopups(pt);
363
public JComponent getTargetComponent() {
364
return targetComponent;
367
private void completeInterMenuDrag(Point pt) {
368
p("================\n\n\n\n==========\n\n========");
369
p("complete inter menu drag: target comp = " + targetComponent);
370
if(targetComponent == null) return;
372
//check if it's still a valid target
373
JComponent tcomp = (JComponent) getDeepestComponent(pt);
374
p("target = " + targetComponent);
375
p("tcomp = " + tcomp);
376
if(targetComponent != tcomp) {
377
p("no longer over a valid target. bailing");
378
menuEditLayer.formDesigner.toggleSelectionMode();
382
// conver to target component's coords.
383
Point pt2 = SwingUtilities.convertPoint(menuEditLayer.glassLayer, pt, tcomp);
384
if(tcomp instanceof JMenu) {
385
JMenu menu = (JMenu) tcomp;
388
// if dragging a jmenu onto a toplevel jmenu
389
if(menu.getParent() instanceof JMenuBar && isOnlyJMenus(payloadComponents)) { //payloadComponent instanceof JMenu) {
390
p("dropping into a toplevel menu");
391
if(DropTargetLayer.isMenuLeftEdge(pt2, menu)) {
392
p("doing a left drop");
393
menuEditLayer.moveRadComponentToBefore(payloadComponent, menu);
395
} else if (DropTargetLayer.isMenuRightEdge(pt2, menu)) {
396
p("doing a right drop");
397
menuEditLayer.moveRadComponentToAfter(payloadComponent, menu);
399
} else { // else must be in the center so just add to the menu instead of next to
400
p("doing a center drop");
401
menuEditLayer.moveRadComponentInto(payloadComponent, menu);
405
p("on a jmenu. could be in or above");
406
p("converted point = " + pt2);
407
if(DropTargetLayer.isSubMenuRightEdge(pt2, menu)) {
408
p("doing 'in' menu drop");
409
menuEditLayer.moveRadComponentInto(payloadComponent, menu);
411
p("doing above/below menu drop");
412
if(DropTargetLayer.isBelowItem(pt2,menu)) {
413
menuEditLayer.moveRadComponentToAfter(payloadComponent, targetComponent);
415
menuEditLayer.moveRadComponentToBefore(payloadComponent, targetComponent);
421
if(tcomp instanceof JMenuBar) {
422
p("dragging to the menubar itself");
423
if(payloadComponent instanceof JMenu) {
424
p("adding menu to the end of the menubar");
425
menuEditLayer.moveRadComponentInto(payloadComponent, targetComponent);
428
p("can't drag a non-JMenu to the menubar");
433
//if after or before the current item
434
if(DropTargetLayer.isBelowItem(pt2,tcomp)) {
435
menuEditLayer.moveRadComponentToAfter(payloadComponent, targetComponent);
437
menuEditLayer.moveRadComponentToBefore(payloadComponent, targetComponent);
441
private void completePickAndPlopFromPalette(Point pt, boolean clear) {
442
p("complete pick and plop from palette: target comp = " + targetComponent);
443
PaletteItem paletteItem = PaletteUtils.getSelectedItem();
444
if(paletteItem == null) return;
446
if(targetComponent == null) return;
449
//check if it's still a valid target
450
JComponent tcomp = (JComponent) getDeepestComponent(pt);
451
p("target = " + targetComponent);
452
p("tcomp = " + tcomp);
453
if(targetComponent != tcomp) {
454
p("no longer over a valid target. bailing");
456
menuEditLayer.formDesigner.toggleSelectionMode();
461
JComponent newComponent = null;
462
// get the pre-created component
463
MetaComponentCreator creator = menuEditLayer.formDesigner.getFormModel().getComponentCreator();
464
if(creator != null) {
465
RADVisualComponent precreated = creator.precreateVisualComponent(
466
paletteItem.getComponentClassSource());
467
if(precreated != null) {
468
newComponent = (JComponent) precreated.getBeanInstance();
471
// if pre-creation failed then make new component manually
472
if(newComponent == null) {
474
newComponent = (JComponent)paletteItem.getComponentClass().newInstance();
475
} catch (Exception ex) {
476
p("couldn't create new component!");
477
ex.printStackTrace();
482
// add new component reference to the form
483
// i can probably remove both of these variables
484
LayoutComponent layoutComponent = creator.getPrecreatedLayoutComponent();
485
Object constraints = null;
488
Point pt2 = SwingUtilities.convertPoint(menuEditLayer.glassLayer, pt, targetComponent);
489
// dragged to a menu, add inside the menu instead of next to it
490
if(targetComponent instanceof JMenu) {
491
p("============== doing a new comp to a jmenu");
494
if(targetComponent.getParent() instanceof JMenuBar) {
495
p("on top of a toplevel menu");
497
if(DropTargetLayer.isMenuLeftEdge(pt2, targetComponent) && isMenuPayload(creator)) {
498
p("on the left edge");
499
RADVisualComponent newRad = creator.getPrecreatedMetaComponent();
500
menuEditLayer.addRadComponentToBefore(newRad, targetComponent);
501
} else if(DropTargetLayer.isMenuRightEdge(pt2, targetComponent) && isMenuPayload(creator)) {
502
p("on the right edge");
503
RADVisualComponent newRad = creator.getPrecreatedMetaComponent();
504
menuEditLayer.addRadComponentToAfter(newRad, targetComponent);
506
p("else in the center");
507
menuEditLayer.addRadComponentToEnd(targetComponent, creator);
510
if(DropTargetLayer.isSubMenuRightEdge(pt2, targetComponent)) {
511
p("doing in menu drop");
512
menuEditLayer.addRadComponentToEnd(targetComponent, creator);
514
p("doing above/below menu drop");
515
RADVisualComponent newRad = creator.getPrecreatedMetaComponent();
516
p("new rad = " + newRad);
517
if(DropTargetLayer.isBelowItem(pt2, targetComponent)) {
518
menuEditLayer.addRadComponentToAfter(newRad, targetComponent);
520
menuEditLayer.addRadComponentToBefore(newRad, targetComponent);
525
if(targetComponent instanceof JMenuBar) {
526
p("======= doing a new comp directly to the jmenubar");
527
menuEditLayer.addRadComponentToEnd(targetComponent, creator);
529
// add the new component to the target's containing menu
530
p("doing the new kind of add");
531
RADVisualComponent newRad = creator.getPrecreatedMetaComponent();
532
p("new rad = " + newRad);
533
//menuEditLayer.addRadComponentToBefore(newRad, targetComponent);
534
if(DropTargetLayer.isBelowItem(pt2, targetComponent)) {
535
menuEditLayer.addRadComponentToAfter(newRad, targetComponent);
537
menuEditLayer.addRadComponentToBefore(newRad, targetComponent);
543
menuEditLayer.formDesigner.toggleSelectionMode();
548
private boolean isOnlyJMenus(List<JMenuItem> items) {
549
for(JMenuItem item : items) {
550
if(item instanceof JMenu) continue;
556
private boolean isMenuPayload(MetaComponentCreator creator) {
557
if(JMenu.class.isAssignableFrom(creator.getPrecreatedMetaComponent().getBeanClass())) {
563
//josh: this is a very slow way to find the component under the mouse cursor.
564
//there must be a faster way to do it
565
public JComponent getDeepestComponentInPopups(Point pt) {
566
Component[] popups = menuEditLayer.layers.getComponentsInLayer(JLayeredPane.DEFAULT_LAYER);
567
for(Component popup : popups) {
568
//p("looking at popup: " + popup);
569
if(popup.isVisible()) {
570
Point pt2 = SwingUtilities.convertPoint(menuEditLayer, pt, popup);
571
JComponent child = (JComponent) javax.swing.SwingUtilities.getDeepestComponentAt(popup, pt2.x, pt2.y);
572
if(child != null) return child;
579
public boolean isStarted() {
583
public PaletteItem getCurrentItem() {
587
private static void p(String s) {
589
System.out.println(s);