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.
16 package com
.intellij
.uiDesigner
.palette
;
18 import com
.intellij
.ide
.dnd
.DnDDragStartBean
;
19 import com
.intellij
.ide
.palette
.PaletteItem
;
20 import com
.intellij
.openapi
.actionSystem
.ActionGroup
;
21 import com
.intellij
.openapi
.actionSystem
.ActionManager
;
22 import com
.intellij
.openapi
.actionSystem
.DataConstants
;
23 import com
.intellij
.openapi
.actionSystem
.DataKey
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.module
.ResourceFileUtil
;
26 import com
.intellij
.openapi
.project
.Project
;
27 import com
.intellij
.openapi
.util
.IconLoader
;
28 import com
.intellij
.openapi
.vfs
.VirtualFile
;
29 import com
.intellij
.psi
.JavaPsiFacade
;
30 import com
.intellij
.psi
.PsiFile
;
31 import com
.intellij
.psi
.search
.GlobalSearchScope
;
32 import com
.intellij
.ui
.ColoredListCellRenderer
;
33 import com
.intellij
.ui
.SimpleTextAttributes
;
34 import com
.intellij
.uiDesigner
.HSpacer
;
35 import com
.intellij
.uiDesigner
.UIDesignerBundle
;
36 import com
.intellij
.uiDesigner
.VSpacer
;
37 import com
.intellij
.uiDesigner
.binding
.FormClassIndex
;
38 import com
.intellij
.uiDesigner
.core
.GridConstraints
;
39 import com
.intellij
.uiDesigner
.lw
.StringDescriptor
;
40 import com
.intellij
.uiDesigner
.propertyInspector
.IntrospectedProperty
;
41 import com
.intellij
.uiDesigner
.radComponents
.RadAtomicComponent
;
42 import org
.jetbrains
.annotations
.NonNls
;
43 import org
.jetbrains
.annotations
.NotNull
;
44 import org
.jetbrains
.annotations
.Nullable
;
48 import java
.io
.IOException
;
50 import java
.util
.List
;
53 * @author Anton Katilin
54 * @author Vladimir Kondratyev
56 public final class ComponentItem
implements Cloneable
, PaletteItem
{
57 private static final Logger LOG
= Logger
.getInstance("#com.intellij.uiDesigner.palette.ComponentItem");
59 public static final DataKey
<ComponentItem
> DATA_KEY
= DataKey
.create(ComponentItem
.class.getName());
61 @NonNls private String myClassName
;
62 private final GridConstraints myDefaultConstraints
;
64 * Do not use this member directly. Use {@link #getIcon()} instead.
68 * Do not use this member directly. Use {@link #getSmallIcon()} instead.
70 private Icon mySmallIcon
;
73 * @see #setIconPath(java.lang.String)
75 private String myIconPath
;
77 * Do not access this field directly. Use {@link #getToolTipText()} instead.
79 final String myToolTipText
;
80 private final HashMap
<String
, StringDescriptor
> myPropertyName2initialValue
;
81 /** Whether item is removable or not */
82 private final boolean myRemovable
;
84 private boolean myAutoCreateBinding
;
85 private boolean myCanAttachLabel
;
86 private boolean myIsContainer
;
87 private boolean myAnyComponent
;
88 private Dimension myInitialSize
;
90 @NotNull private final Project myProject
;
93 @NotNull Project project
,
94 @NotNull final String className
,
95 @Nullable final String iconPath
,
96 @Nullable final String toolTipText
,
97 @NotNull final GridConstraints defaultConstraints
,
98 @NotNull final HashMap
<String
, StringDescriptor
> propertyName2initialValue
,
99 final boolean removable
,
100 final boolean autoCreateBinding
,
101 final boolean canAttachLabel
103 myAutoCreateBinding
= autoCreateBinding
;
104 myCanAttachLabel
= canAttachLabel
;
106 setClassName(className
);
107 setIconPath(iconPath
);
109 myToolTipText
= toolTipText
;
110 myDefaultConstraints
= defaultConstraints
;
111 myPropertyName2initialValue
= propertyName2initialValue
;
113 myRemovable
= removable
;
117 * @return whether the item is removable from palette or not.
119 public boolean isRemovable() {
123 private static String
calcToolTipText(@NotNull final String className
) {
124 final int lastDotIndex
= className
.lastIndexOf('.');
125 if (lastDotIndex
!= -1 && lastDotIndex
!= className
.length() - 1/*not the last char in class name*/) {
126 return className
.substring(lastDotIndex
+ 1) + " (" + className
.substring(0, lastDotIndex
) + ")";
133 /** Creates deep copy of the object. You can edit any properties of the returned object. */
134 public ComponentItem
clone(){
135 final ComponentItem result
= new ComponentItem(
140 (GridConstraints
)myDefaultConstraints
.clone(),
141 (HashMap
<String
, StringDescriptor
>)myPropertyName2initialValue
.clone(),
146 result
.setIsContainer(myIsContainer
);
151 * @return string that represents path in the JAR file system that was used to load
152 * icon returned by {@link #getIcon()} method. This method can returns <code>null</code>.
153 * It means that palette item has some "unknown" item.
155 @Nullable String
getIconPath() {
160 * @param iconPath new path inside JAR file system. <code>null</code> means that
161 * <code>iconPath</code> is not specified and some "unknown" icon should be used
162 * to represent the {@link ComponentItem} in UI.
164 void setIconPath(@Nullable final String iconPath
){
165 myIcon
= null; // reset cached icon
166 mySmallIcon
= null; // reset cached icon
168 myIconPath
= iconPath
;
172 * @return item's icon. This icon is used to represent item at the toolbar.
173 * Note, that the method never returns <code>null</code>. It returns some
174 * default "unknown" icon for the items that has no specified icon in the XML.
176 @NotNull public Icon
getIcon() {
177 // Check cached value first
183 if(myIconPath
!= null && myIconPath
.length() > 0) {
184 final VirtualFile iconFile
= ResourceFileUtil
.findResourceFileInScope(myIconPath
, myProject
, GlobalSearchScope
.allScope(myProject
));
185 if (iconFile
!= null) {
187 myIcon
= new ImageIcon(iconFile
.contentsToByteArray());
189 catch (IOException e
) {
194 myIcon
= IconLoader
.findIcon(myIconPath
);
198 myIcon
= IconLoader
.getIcon("/com/intellij/uiDesigner/icons/unknown.png");
200 LOG
.assertTrue(myIcon
!= null);
205 * @return small item's icon. This icon represents component in the
206 * component tree. The method never returns <code>null</code>. It returns some
207 * default "unknown" icon for the items that has no specified icon in the XML.
209 @NotNull public Icon
getSmallIcon() {
210 // Check cached value first
211 if(mySmallIcon
!= null){
215 // [vova] It's safe to cast to ImageIcon here because all icons loaded by IconLoader
217 final Icon icon
= getIcon();
218 if (icon
instanceof ImageIcon
) {
219 final ImageIcon imageIcon
= (ImageIcon
)icon
;
220 mySmallIcon
= new MySmallIcon(imageIcon
.getImage());
230 * @return name of component's class which is represented by the item.
232 @NotNull public String
getClassName() {
236 public String
getClassShortName() {
237 final int lastDotIndex
= myClassName
.lastIndexOf('.');
238 if (lastDotIndex
!= -1 && lastDotIndex
!= myClassName
.length() - 1/*not the last char in class name*/) {
239 return myClassName
.substring(lastDotIndex
+ 1).replace('$', '.');
242 return myClassName
.replace('$', '.');
247 * @param className name of the class that will be instanteated when user drop
248 * item on the form. Cannot be <code>null</code>. If the class does not exist or
249 * could not be instanteated (for example, class has no default constructor,
250 * it's not a subclass of JComponent, etc) then placeholder component will be
253 public void setClassName(@NotNull final String className
){
254 myClassName
= className
;
257 public String
getToolTipText() {
258 return myToolTipText
!= null ? myToolTipText
: calcToolTipText(myClassName
);
261 @NotNull public GridConstraints
getDefaultConstraints() {
262 return myDefaultConstraints
;
266 * The method returns initial value of the property. Term
267 * "initial" means that just after creation of RadComponent
268 * all its properties are set into initial values.
269 * The method returns <code>null</code> if the
270 * initial property is not defined. Unfortunately we cannot
271 * put this method into the constuctor of <code>RadComponent</code>.
272 * The problem is that <code>RadComponent</code> is used in the
273 * code genaration and code generation doesn't depend on any
274 * <code>ComponentItem</code>, so we need to initialize <code>RadComponent</code>
275 * in all places where it's needed explicitly.
277 public Object
getInitialValue(final IntrospectedProperty property
){
278 return myPropertyName2initialValue
.get(property
.getName());
282 * Internal method. It should be used only to externalize initial item's values.
283 * This method never returns <code>null</code>.
285 HashMap
<String
, StringDescriptor
> getInitialValues(){
286 return myPropertyName2initialValue
;
289 public boolean isAutoCreateBinding() {
290 return myAutoCreateBinding
;
293 public void setAutoCreateBinding(final boolean autoCreateBinding
) {
294 myAutoCreateBinding
= autoCreateBinding
;
297 public boolean isCanAttachLabel() {
298 return myCanAttachLabel
;
301 public void setCanAttachLabel(final boolean canAttachLabel
) {
302 myCanAttachLabel
= canAttachLabel
;
305 public boolean isContainer() {
306 return myIsContainer
;
309 public void setIsContainer(final boolean isContainer
) {
310 myIsContainer
= isContainer
;
313 public boolean equals(final Object o
) {
314 if (this == o
) return true;
315 if (!(o
instanceof ComponentItem
)) return false;
317 final ComponentItem componentItem
= (ComponentItem
)o
;
319 if (myClassName
!= null ?
!myClassName
.equals(componentItem
.myClassName
) : componentItem
.myClassName
!= null) return false;
320 if (myDefaultConstraints
!= null
321 ?
!myDefaultConstraints
.equals(componentItem
.myDefaultConstraints
)
322 : componentItem
.myDefaultConstraints
!= null) {
325 if (myIconPath
!= null ?
!myIconPath
.equals(componentItem
.myIconPath
) : componentItem
.myIconPath
!= null) return false;
326 if (myPropertyName2initialValue
!= null
327 ?
!myPropertyName2initialValue
.equals(componentItem
.myPropertyName2initialValue
)
328 : componentItem
.myPropertyName2initialValue
!= null) {
331 if (myToolTipText
!= null ?
!myToolTipText
.equals(componentItem
.myToolTipText
) : componentItem
.myToolTipText
!= null) return false;
336 public int hashCode() {
338 result
= (myClassName
!= null ? myClassName
.hashCode() : 0);
339 result
= 29 * result
+ (myDefaultConstraints
!= null ? myDefaultConstraints
.hashCode() : 0);
340 result
= 29 * result
+ (myIconPath
!= null ? myIconPath
.hashCode() : 0);
341 result
= 29 * result
+ (myToolTipText
!= null ? myToolTipText
.hashCode() : 0);
342 result
= 29 * result
+ (myPropertyName2initialValue
!= null ? myPropertyName2initialValue
.hashCode() : 0);
346 public void customizeCellRenderer(ColoredListCellRenderer cellRenderer
, boolean selected
, boolean hasFocus
) {
347 cellRenderer
.setIcon(getSmallIcon());
348 if (myAnyComponent
) {
349 cellRenderer
.append(UIDesignerBundle
.message("palette.non.palette.component"), SimpleTextAttributes
.REGULAR_ATTRIBUTES
);
350 cellRenderer
.setToolTipText(UIDesignerBundle
.message("palette.non.palette.component.tooltip"));
353 cellRenderer
.append(getClassShortName(), SimpleTextAttributes
.REGULAR_ATTRIBUTES
);
354 cellRenderer
.setToolTipText(getToolTipText());
358 @Nullable public DnDDragStartBean
startDragging() {
359 if (isAnyComponent()) return null;
360 return new DnDDragStartBean(this);
363 @Nullable public ActionGroup
getPopupActionGroup() {
364 return (ActionGroup
) ActionManager
.getInstance().getAction("GuiDesigner.PaletteComponentPopupMenu");
367 @Nullable public Object
getData(Project project
, String dataId
) {
368 if (dataId
.equals(DataConstants
.PSI_ELEMENT
)) {
369 return JavaPsiFacade
.getInstance(project
).findClass(myClassName
, GlobalSearchScope
.allScope(project
));
371 if (dataId
.equals(getClass().getName())) {
374 if (dataId
.equals(GroupItem
.class.getName())) {
375 return Palette
.getInstance(project
).findGroup(this);
380 @Nullable public PsiFile
getBoundForm() {
381 if (myClassName
.length() == 0 || myClassName
.startsWith("javax.swing")) {
384 List
<PsiFile
> boundForms
= FormClassIndex
.findFormsBoundToClass(myProject
, myClassName
.replace('$', '.'));
385 if (boundForms
.size() > 0) {
386 return boundForms
.get(0);
392 public Dimension
getInitialSize(final JComponent parent
, final ClassLoader loader
) {
393 if (myInitialSize
!= null) {
394 return myInitialSize
;
396 myInitialSize
= new Dimension(myDefaultConstraints
.myPreferredSize
);
397 if (myInitialSize
.width
<= 0 || myInitialSize
.height
<= 0) {
399 Class aClass
= Class
.forName(getClassName(), true, loader
);
400 RadAtomicComponent component
= new RadAtomicComponent(aClass
, "", Palette
.getInstance(myProject
));
401 component
.initDefaultProperties(this);
402 final JComponent delegee
= component
.getDelegee();
403 if (parent
!= null) {
404 final Font font
= parent
.getFont();
405 delegee
.setFont(font
);
407 Dimension prefSize
= delegee
.getPreferredSize();
408 Dimension minSize
= delegee
.getMinimumSize();
409 if (myInitialSize
.width
<= 0) {
410 myInitialSize
.width
= prefSize
.width
;
412 if (myInitialSize
.height
<= 0) {
413 myInitialSize
.height
= prefSize
.height
;
415 myInitialSize
.width
= Math
.max(myInitialSize
.width
, minSize
.width
);
416 myInitialSize
.height
= Math
.max(myInitialSize
.height
, minSize
.height
);
418 catch (Exception e
) {
422 return myInitialSize
;
425 public static ComponentItem
createAnyComponentItem(final Project project
) {
426 ComponentItem result
= new ComponentItem(project
, "", null, null,
427 new GridConstraints(), new HashMap
<String
, StringDescriptor
>(),
428 false, false, false);
429 result
.myAnyComponent
= true;
433 public boolean isAnyComponent() {
434 return myAnyComponent
;
437 public boolean isSpacer() {
438 return myClassName
.equals(HSpacer
.class.getName()) || myClassName
.equals(VSpacer
.class.getName());
441 private static final class MySmallIcon
implements Icon
{
442 private final Image myImage
;
444 public MySmallIcon(@NotNull final Image delegate
) {
448 public int getIconHeight() {
452 public int getIconWidth() {
456 public void paintIcon(final Component c
, final Graphics g
, final int x
, final int y
) {
457 g
.drawImage(myImage
, 2, 2, 14, 14, c
);