update copyright
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / palette / ComponentItem.java
blob8b5ff16c0faff9b97b5f986d24c66080e3689e2d
1 /*
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;
46 import javax.swing.*;
47 import java.awt.*;
48 import java.io.IOException;
49 import java.util.*;
50 import java.util.List;
52 /**
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;
63 /**
64 * Do not use this member directly. Use {@link #getIcon()} instead.
66 private Icon myIcon;
67 /**
68 * Do not use this member directly. Use {@link #getSmallIcon()} instead.
70 private Icon mySmallIcon;
71 /**
72 * @see #getIconPath()
73 * @see #setIconPath(java.lang.String)
75 private String myIconPath;
76 /**
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;
92 public ComponentItem(
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;
105 myProject = project;
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() {
120 return myRemovable;
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) + ")";
128 else{
129 return className;
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(
136 myProject,
137 myClassName,
138 myIconPath,
139 myToolTipText,
140 (GridConstraints)myDefaultConstraints.clone(),
141 (HashMap<String, StringDescriptor>)myPropertyName2initialValue.clone(),
142 myRemovable,
143 myAutoCreateBinding,
144 myCanAttachLabel
146 result.setIsContainer(myIsContainer);
147 return result;
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() {
156 return myIconPath;
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
178 if(myIcon != null){
179 return myIcon;
182 // Create new icon
183 if(myIconPath != null && myIconPath.length() > 0) {
184 final VirtualFile iconFile = ResourceFileUtil.findResourceFileInScope(myIconPath, myProject, GlobalSearchScope.allScope(myProject));
185 if (iconFile != null) {
186 try {
187 myIcon = new ImageIcon(iconFile.contentsToByteArray());
189 catch (IOException e) {
190 myIcon = null;
193 else {
194 myIcon = IconLoader.findIcon(myIconPath);
197 if(myIcon == null){
198 myIcon = IconLoader.getIcon("/com/intellij/uiDesigner/icons/unknown.png");
200 LOG.assertTrue(myIcon != null);
201 return myIcon;
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){
212 return myIcon;
215 // [vova] It's safe to cast to ImageIcon here because all icons loaded by IconLoader
216 // are ImageIcon(s).
217 final Icon icon = getIcon();
218 if (icon instanceof ImageIcon) {
219 final ImageIcon imageIcon = (ImageIcon)icon;
220 mySmallIcon = new MySmallIcon(imageIcon.getImage());
222 else {
223 mySmallIcon = icon;
226 return mySmallIcon;
230 * @return name of component's class which is represented by the item.
232 @NotNull public String getClassName() {
233 return myClassName;
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('$', '.');
241 else{
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
251 * added to the form.
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) {
323 return false;
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) {
329 return false;
331 if (myToolTipText != null ? !myToolTipText.equals(componentItem.myToolTipText) : componentItem.myToolTipText != null) return false;
333 return true;
336 public int hashCode() {
337 int result;
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);
343 return result;
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"));
352 else {
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())) {
372 return this;
374 if (dataId.equals(GroupItem.class.getName())) {
375 return Palette.getInstance(project).findGroup(this);
377 return null;
380 @Nullable public PsiFile getBoundForm() {
381 if (myClassName.length() == 0 || myClassName.startsWith("javax.swing")) {
382 return null;
384 List<PsiFile> boundForms = FormClassIndex.findFormsBoundToClass(myProject, myClassName.replace('$', '.'));
385 if (boundForms.size() > 0) {
386 return boundForms.get(0);
388 return null;
391 @NotNull
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) {
398 try {
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) {
419 LOG.debug(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;
430 return result;
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) {
445 myImage = delegate;
448 public int getIconHeight() {
449 return 18;
452 public int getIconWidth() {
453 return 18;
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);