2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.uiDesigner
.actions
;
19 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.uiDesigner
.CutCopyPasteSupport
;
22 import com
.intellij
.uiDesigner
.FormEditingUtil
;
23 import com
.intellij
.uiDesigner
.UIDesignerBundle
;
24 import com
.intellij
.uiDesigner
.core
.GridConstraints
;
25 import com
.intellij
.uiDesigner
.designSurface
.GuiEditor
;
26 import com
.intellij
.uiDesigner
.lw
.IProperty
;
27 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.BindingProperty
;
28 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.IntroComponentProperty
;
29 import com
.intellij
.uiDesigner
.radComponents
.RadComponent
;
30 import com
.intellij
.uiDesigner
.radComponents
.RadContainer
;
31 import gnu
.trove
.TIntHashSet
;
32 import org
.jetbrains
.annotations
.NotNull
;
33 import org
.jetbrains
.annotations
.Nullable
;
40 public class DuplicateComponentsAction
extends AbstractGuiEditorAction
{
41 private static final Logger LOG
= Logger
.getInstance("#com.intellij.uiDesigner.actions.DuplicateComponentsAction");
43 public DuplicateComponentsAction() {
47 protected void actionPerformed(final GuiEditor editor
, final List
<RadComponent
> selection
, final AnActionEvent e
) {
48 FormEditingUtil
.remapToActionTargets(selection
);
49 RadContainer parent
= FormEditingUtil
.getSelectionParent(selection
);
50 assert parent
!= null;
51 List
<RadComponent
> duplicates
= new ArrayList
<RadComponent
>();
52 Map
<RadComponent
, RadComponent
> duplicateMap
= new HashMap
<RadComponent
, RadComponent
>();
53 TIntHashSet insertedRows
= new TIntHashSet();
54 boolean incrementRow
= true;
55 if (selection
.size() > 1 && canDuplicate(selection
, false) && FormEditingUtil
.getSelectionBounds(selection
).width
== 1) {
58 for(RadComponent c
: selection
) {
59 final int row
= c
.getConstraints().getCell(incrementRow
);
60 int rowSpan
= c
.getConstraints().getSpan(incrementRow
);
61 int insertIndex
= parent
.indexOfComponent(c
);
62 if (parent
.getLayoutManager().isGrid()) {
63 if (!insertedRows
.contains(row
) && !isSpaceBelowEmpty(c
, incrementRow
)) {
64 insertedRows
.add(row
);
65 parent
.getGridLayoutManager().copyGridCells(parent
, parent
, incrementRow
, row
, rowSpan
, row
+ rowSpan
);
69 List
<RadComponent
> copyList
= CutCopyPasteSupport
.copyComponents(editor
, Collections
.singletonList(c
));
70 if (copyList
!= null) {
71 RadComponent copy
= copyList
.get(0);
72 if (parent
.getLayoutManager().isGrid()) {
73 copy
.getConstraints().setCell(incrementRow
, row
+ rowSpan
+ parent
.getGridLayoutManager().getGapCellCount());
74 copy
.getConstraints().setSpan(incrementRow
, rowSpan
);
76 parent
.addComponent(copy
, insertIndex
+1);
77 fillDuplicateMap(duplicateMap
, c
, copy
);
81 adjustDuplicates(duplicateMap
);
82 FormEditingUtil
.selectComponents(editor
, duplicates
);
85 private static void fillDuplicateMap(Map
<RadComponent
, RadComponent
> duplicates
, final RadComponent c
, final RadComponent copy
) {
86 duplicates
.put(c
, copy
);
87 if (c
instanceof RadContainer
) {
88 LOG
.assertTrue(copy
instanceof RadContainer
);
89 final RadContainer container
= (RadContainer
)c
;
90 final RadContainer containerCopy
= (RadContainer
)copy
;
91 for(int i
=0; i
<container
.getComponentCount(); i
++) {
92 fillDuplicateMap(duplicates
, container
.getComponent(i
), containerCopy
.getComponent(i
));
97 private static void adjustDuplicates(final Map
<RadComponent
, RadComponent
> duplicates
) {
98 for(RadComponent c
: duplicates
.keySet()) {
99 RadComponent copy
= duplicates
.get(c
);
100 if (c
.getBinding() != null) {
101 String binding
= BindingProperty
.getDefaultBinding(copy
);
102 new BindingProperty(c
.getProject()).setValueEx(copy
, binding
);
103 copy
.setDefaultBinding(true);
105 for(IProperty prop
: copy
.getModifiedProperties()) {
106 if (prop
instanceof IntroComponentProperty
) {
107 final IntroComponentProperty componentProperty
= (IntroComponentProperty
)prop
;
108 String copyValue
= componentProperty
.getValue(copy
);
109 for(RadComponent original
: duplicates
.keySet()) {
110 if (original
.getId().equals(copyValue
)) {
111 componentProperty
.setValueEx(copy
, duplicates
.get(original
).getId());
119 private static boolean isSpaceBelowEmpty(final RadComponent component
, boolean incrementRow
) {
120 final GridConstraints constraints
= component
.getConstraints();
121 int startRow
= constraints
.getCell(incrementRow
) + constraints
.getSpan(incrementRow
);
122 int endRow
= constraints
.getCell(incrementRow
) + constraints
.getSpan(incrementRow
)*2 +
123 component
.getParent().getGridLayoutManager().getGapCellCount();
124 if (endRow
> component
.getParent().getGridCellCount(incrementRow
)) {
127 for(int row
=startRow
; row
< endRow
; row
++) {
128 for(int col
=constraints
.getCell(!incrementRow
); col
< constraints
.getCell(!incrementRow
) + constraints
.getSpan(!incrementRow
); col
++) {
129 if (component
.getParent().getComponentAtGrid(incrementRow
, row
, col
) != null) {
137 protected void update(@NotNull GuiEditor editor
, final ArrayList
<RadComponent
> selection
, final AnActionEvent e
) {
138 FormEditingUtil
.remapToActionTargets(selection
);
139 final RadContainer parent
= FormEditingUtil
.getSelectionParent(selection
);
140 e
.getPresentation().setEnabled(parent
!= null && (parent
.getLayoutManager().isGrid() || parent
.getLayoutManager().isIndexed()));
141 // The action is enabled in any of the following cases:
142 // 1) a single component is selected;
143 // 2) all selected components have rowspan=1
144 // 3) all selected components have the same row and rowspan
145 if (selection
.size() > 1 && parent
!= null && parent
.getLayoutManager().isGrid()) {
146 e
.getPresentation().setEnabled(canDuplicate(selection
, true) || canDuplicate(selection
, false));
150 private static boolean canDuplicate(final List
<RadComponent
> selection
, final boolean incrementRow
) {
151 int aRow
= selection
.get(0).getConstraints().getCell(incrementRow
);
152 int aRowSpan
= selection
.get(0).getConstraints().getSpan(incrementRow
);
153 for(int i
=1; i
<selection
.size(); i
++) {
154 final RadComponent c
= selection
.get(i
);
155 if (c
.getConstraints().getSpan(incrementRow
) > 1 || aRowSpan
> 1) {
156 if (c
.getConstraints().getCell(incrementRow
) != aRow
|| c
.getConstraints().getSpan(incrementRow
) != aRowSpan
) {
165 protected String
getCommandName() {
166 return UIDesignerBundle
.message("command.duplicate");