IDEA-34482
[idea/community.git] / plugins / ui-designer / src / com / intellij / uiDesigner / radComponents / RadComponent.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.uiDesigner.radComponents;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.uiDesigner.UIFormXmlConstants;
22 import com.intellij.uiDesigner.XmlWriter;
23 import com.intellij.uiDesigner.SwingProperties;
24 import com.intellij.uiDesigner.StringDescriptorManager;
25 import com.intellij.uiDesigner.compiler.Utils;
26 import com.intellij.uiDesigner.core.GridConstraints;
27 import com.intellij.uiDesigner.core.Util;
28 import com.intellij.uiDesigner.designSurface.EventProcessor;
29 import com.intellij.uiDesigner.designSurface.GuiEditor;
30 import com.intellij.uiDesigner.designSurface.InsertComponentProcessor;
31 import com.intellij.uiDesigner.lw.*;
32 import com.intellij.uiDesigner.palette.ComponentItem;
33 import com.intellij.uiDesigner.palette.Palette;
34 import com.intellij.uiDesigner.propertyInspector.IntrospectedProperty;
35 import com.intellij.uiDesigner.propertyInspector.Property;
36 import com.intellij.uiDesigner.propertyInspector.properties.ClientPropertiesProperty;
37 import com.intellij.uiDesigner.propertyInspector.properties.ClientPropertyProperty;
38 import com.intellij.uiDesigner.propertyInspector.properties.IntroStringProperty;
39 import com.intellij.uiDesigner.snapShooter.SnapshotContext;
40 import com.intellij.util.ArrayUtil;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import javax.swing.*;
46 import java.awt.Dimension;
47 import java.awt.Point;
48 import java.awt.Rectangle;
49 import java.awt.event.MouseEvent;
50 import java.beans.PropertyChangeListener;
51 import java.beans.PropertyChangeSupport;
52 import java.lang.reflect.Constructor;
53 import java.util.ArrayList;
54 import java.util.HashSet;
55
56 /**
57  * @author Anton Katilin
58  * @author Vladimir Kondratyev
59  */
60 public abstract class RadComponent implements IComponent {
61   private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.radComponents.RadComponent");
62
63   /**
64    * Shared instance of empty array of RadComponenets
65    */
66   public static final RadComponent[] EMPTY_ARRAY = new RadComponent[]{};
67   /**
68    * Using this constant as client property of the Swing component
69    * you can find corresponding <code>RadComponent</code>
70    */
71   @NonNls
72   public static final String CLIENT_PROP_RAD_COMPONENT = "radComponent";
73
74   @NonNls
75   public static final String CLIENT_PROP_LOAD_TIME_LOCALE = "LoadTimeLocaleKey";
76
77   /**
78    * Whether the component selected or not. Value is java.lang.Boolean
79    */
80   @NonNls public static final String PROP_SELECTED = "selected";
81
82   /**
83    * Change notification for this property is fired when the constraints of a component
84    * change.
85    */
86   @NonNls public static final String PROP_CONSTRAINTS = "constraints";
87
88   /**
89    * Component id is unique per RadRootContainer.
90    */
91   @NotNull private final String myId;
92   /**
93    * @see #getBinding()
94    */
95   private String myBinding;
96   private boolean myCustomCreate = false;
97   private boolean myLoadingProperties = false;
98
99   @NotNull private final Module myModule;
100   @NotNull private final Class myClass;
101   /**
102    * Delegee is the JComponent which really represents the
103    * component in UI.
104    */
105   @NotNull private final JComponent myDelegee;
106   /**
107    * Parent RadContainer. This field is always not <code>null</code>
108    * is the component is in hierarchy. But the root of hierarchy
109    * has <code>null</code> parent indeed.
110    */
111   private RadContainer myParent;
112   /**
113    * Defines whether the component selected or not.
114    */
115   private boolean mySelected;
116
117   @NotNull private final GridConstraints myConstraints;
118
119   private Object myCustomLayoutConstraints;
120
121   private final PropertyChangeSupport myChangeSupport;
122
123   private final HashSet<String> myModifiedPropertyNames;
124
125   private Palette myPalette;
126
127   private boolean myHasDragger;
128   private boolean myResizing;
129   private boolean myDragging;
130   private boolean myDragBorder;
131   private boolean myDefaultBinding;
132
133   /**
134    * Creates new <code>RadComponent</code> with the specified
135    * class of delegee and specified ID.
136    *
137    * @param aClass class of the compoent's delegee
138    * @param id     id of the compoent inside the form. <code>id</code>
139    *               should be a unique atring inside the form.
140    */
141   public RadComponent(final Module module, @NotNull final Class aClass, @NotNull final String id) {
142     myModule = module;
143     myClass = aClass;
144     myId = id;
145
146     myChangeSupport = new PropertyChangeSupport(this);
147     myConstraints = new GridConstraints();
148     myModifiedPropertyNames = new HashSet<String>();
149
150     Constructor constructor;
151     try {
152       constructor = myClass.getConstructor(ArrayUtil.EMPTY_CLASS_ARRAY);
153     }
154     catch (NoSuchMethodException e) {
155       try {
156         constructor = Utils.suggestReplacementClass(myClass).getConstructor(ArrayUtil.EMPTY_CLASS_ARRAY);
157       }
158       catch (NoSuchMethodException e1) {
159         throw new RuntimeException(e1);
160       }
161       setCustomCreate(true);
162     }
163
164     constructor.setAccessible(true);
165     try {
166       myDelegee = (JComponent)constructor.newInstance(ArrayUtil.EMPTY_OBJECT_ARRAY);
167     }
168     catch (Exception e) {
169       throw new RuntimeException(e);
170     }
171
172     myDelegee.putClientProperty(CLIENT_PROP_RAD_COMPONENT, this);
173   }
174
175   public RadComponent(final Module module, @NotNull final Class aClass, @NotNull final String id, final Palette palette) {
176     this(module, aClass, id);
177     myPalette = palette;
178   }
179
180   /**
181    * @return module for the component.
182    */
183   public final Module getModule() {
184     return myModule;
185   }
186
187   public boolean isLoadingProperties() {
188     return myLoadingProperties;
189   }
190
191   @NotNull
192   public final Project getProject() {
193     return myModule.getProject();
194   }
195
196   public Palette getPalette() {
197     if (myPalette == null) {
198       return Palette.getInstance(getProject());
199     }
200     return myPalette;
201   }
202
203   public void setPalette(final Palette palette) {
204     myPalette = palette;
205   }
206
207   /**
208    * Initializes introspected properties into default values and
209    * sets default component's constraints.
210    */
211   public void init(final GuiEditor editor, @NotNull final ComponentItem item) {
212     initDefaultProperties(item);
213   }
214
215   public void initDefaultProperties(@NotNull final ComponentItem item) {
216     final IntrospectedProperty[] properties = getPalette().getIntrospectedProperties(this);
217     for (final IntrospectedProperty property : properties) {
218       final Object initialValue = item.getInitialValue(property);
219       if (initialValue != null) {
220         try {
221           //noinspection unchecked
222           property.setValue(this, initialValue);
223         }
224         catch (Exception e) {
225           throw new RuntimeException(e);
226         }
227       }
228     }
229
230     myConstraints.restore(item.getDefaultConstraints());
231   }
232
233   /**
234    * @return the component's id. It is unique within the form.
235    */
236   @NotNull
237   public final String getId() {
238     return myId;
239   }
240
241   public final String getBinding() {
242     return myBinding;
243   }
244
245   public final void setBinding(final String binding) {
246     //TODO[anton,vova]: check that binding is a valid java identifier!!!
247     myBinding = binding;
248   }
249
250   public boolean isCustomCreate() {
251     return myCustomCreate;
252   }
253
254   public void setCustomCreate(final boolean customCreate) {
255     myCustomCreate = customCreate;
256   }
257
258   public boolean isCustomCreateRequired() {
259     return !getDelegee().getClass().equals(getComponentClass());
260   }
261
262   /**
263    * @return Swing delegee component. The <code>RadComponent</code> has the same
264    *         delegee during all its life.
265    */
266   @NotNull
267   public final JComponent getDelegee() {
268     return myDelegee;
269   }
270
271   /**
272    * Sometime bounds of the inplace editor depends on the point where
273    * user invoked inplace editor.
274    *
275    * @param x x in delegee coordinate system
276    * @param y y in delegee coordinate system
277    * @return inplace property for the <code>RadComponent</code> if any.
278    *         The method returns <code>null</code> if the component doesn't have
279    *         any inplace property. Please not the method can return different
280    *         instances of the property for each invokation.
281    */
282   @Nullable
283   public Property getInplaceProperty(final int x, final int y) {
284     return getDefaultInplaceProperty();
285   }
286
287   @Nullable
288   public Property getDefaultInplaceProperty() {
289     return getPalette().getInplaceProperty(this);
290   }
291
292   @Nullable
293   public Rectangle getDefaultInplaceEditorBounds() {
294     return null;
295   }
296
297   /**
298    * Sometime bounds of the inplace editor depends on the point where
299    * user invoked inplace editor.
300    *
301    * @param x x in delegee coordinate system
302    * @param y y in delegee coordinate system
303    * @return area where editor component is located. This is the hint to the
304    *         designer.  Designer can use or not this rectangle.
305    */
306   @Nullable
307   public Rectangle getInplaceEditorBounds(@NotNull final Property property, final int x, final int y) {
308     return null;
309   }
310
311   @NotNull
312   public final Class getComponentClass() {
313     return myClass;
314   }
315
316   @NotNull
317   public String getComponentClassName() {
318     return myClass.getName();
319   }
320
321   public final Object getCustomLayoutConstraints() {
322     return myCustomLayoutConstraints;
323   }
324
325   public final void setCustomLayoutConstraints(final Object customConstraints) {
326     myCustomLayoutConstraints = customConstraints;
327   }
328
329   public void changeCustomLayoutConstraints(final Object constraints) {
330     setCustomLayoutConstraints(constraints);
331     // update constraints in CardLayout
332     final JComponent parent = getParent().getDelegee();
333     for (int i = 0; i < parent.getComponentCount(); i++) {
334       if (parent.getComponent(i) == getDelegee()) {
335         parent.remove(i);
336         parent.add(getDelegee(), constraints, i);
337         break;
338       }
339     }
340   }
341
342   public final boolean hasDragger() {
343     return myHasDragger;
344   }
345
346   public final void setDragger(final boolean hasDragger) {
347     myHasDragger = hasDragger;
348   }
349
350   public boolean isResizing() {
351     return myResizing;
352   }
353
354   public void setResizing(final boolean resizing) {
355     myResizing = resizing;
356   }
357
358   public boolean isDragging() {
359     return myDragging;
360   }
361
362   public void setDragging(final boolean dragging) {
363     myDragging = dragging;
364     RadContainer parent = getParent();
365     if (parent != null) {
366       parent.getLayoutManager().setChildDragging(this, dragging);
367     }
368   }
369
370   public void setDragBorder(final boolean dragging) {
371     myDragging = dragging;
372     myDragBorder = dragging;
373   }
374
375   public boolean isDragBorder() {
376     return myDragBorder;
377   }
378
379   public boolean isDefaultBinding() {
380     return myDefaultBinding;
381   }
382
383   public void setDefaultBinding(final boolean defaultBinding) {
384     myDefaultBinding = defaultBinding;
385   }
386
387   public final void addPropertyChangeListener(final PropertyChangeListener l) {
388     final PropertyChangeListener[] propertyChangeListeners = myChangeSupport.getPropertyChangeListeners();
389     for (PropertyChangeListener listener : propertyChangeListeners) {
390       assert listener != l;
391     }
392     myChangeSupport.addPropertyChangeListener(l);
393   }
394
395   public final void removePropertyChangeListener(final PropertyChangeListener l) {
396     myChangeSupport.removePropertyChangeListener(l);
397   }
398
399   protected final void firePropertyChanged(
400     @NotNull final String propertyName,
401     final Object oldValue,
402     final Object newValue
403   ) {
404     myChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
405   }
406
407   /**
408    * @return component's constarints.
409    */
410   @NotNull
411   public final GridConstraints getConstraints() {
412     return myConstraints;
413   }
414
415   public final RadContainer getParent() {
416     return myParent;
417   }
418
419   public final void setParent(final RadContainer parent) {
420     myParent = parent;
421   }
422
423   public boolean isSelected() {
424     return mySelected;
425   }
426
427   public void setSelected(final boolean selected) {
428     if (mySelected != selected) {
429       mySelected = selected;
430       firePropertyChanged(PROP_SELECTED, !mySelected, mySelected);
431       GuiEditor.repaintLayeredPane(this);
432     }
433   }
434
435   /**
436    * @see JComponent#getClientProperty(Object)
437    */
438   public final Object getClientProperty(@NotNull final Object key) {
439     return myDelegee.getClientProperty(key);
440   }
441
442   /**
443    * @see JComponent#putClientProperty(Object, Object)
444    */
445   public final void putClientProperty(@NotNull final Object key, final Object value) {
446     myDelegee.putClientProperty(key, value);
447   }
448
449   public final int getX() {
450     return myDelegee.getX();
451   }
452
453   public final int getY() {
454     return myDelegee.getY();
455   }
456
457   public final void setLocation(final Point location) {
458     myDelegee.setLocation(location);
459   }
460
461   public final void shift(final int dx, final int dy) {
462     myDelegee.setLocation(myDelegee.getX() + dx, myDelegee.getY() + dy);
463   }
464
465   public final int getWidth() {
466     return myDelegee.getWidth();
467   }
468
469   public final int getHeight() {
470     return myDelegee.getHeight();
471   }
472
473   public final Dimension getSize() {
474     return myDelegee.getSize();
475   }
476
477   public final void setSize(final Dimension size) {
478     myDelegee.setSize(size);
479   }
480
481   /**
482    * @return bounds of the delegee in the parent container
483    */
484   public final Rectangle getBounds() {
485     return myDelegee.getBounds();
486   }
487
488   public final void setBounds(final Rectangle bounds) {
489     myDelegee.setBounds(bounds);
490   }
491
492   public final Dimension getMinimumSize() {
493     return Util.getMinimumSize(myDelegee, myConstraints, false);
494   }
495
496   public final Dimension getPreferredSize() {
497     return Util.getPreferredSize(myDelegee, myConstraints, false);
498   }
499
500   public void refresh() {
501   }
502
503   public final void revalidate() {
504     RadContainer theContainer = null;
505
506     for (RadContainer container = this instanceof RadContainer ? (RadContainer)this : getParent();
507          container != null;
508          container = container.getParent()) {
509       final RadContainer parent = container.getParent();
510       if (parent != null && parent.isXY()) {
511         final Dimension size = container.getSize();
512         final Dimension minimumSize = container.getMinimumSize();
513         if (size.width < minimumSize.width || size.height < minimumSize.height) {
514           theContainer = container;
515         }
516       }
517     }
518
519     if (theContainer != null) {
520       final Dimension minimumSize = theContainer.getMinimumSize();
521
522       minimumSize.width = Math.max(minimumSize.width, theContainer.getWidth());
523       minimumSize.height = Math.max(minimumSize.height, theContainer.getHeight());
524
525       theContainer.getDelegee().setSize(minimumSize);
526     }
527
528     myDelegee.revalidate();
529   }
530
531   public final boolean isMarkedAsModified(final Property property) {
532     return myModifiedPropertyNames.contains(property.getName());
533   }
534
535   public final void markPropertyAsModified(final Property property) {
536     myModifiedPropertyNames.add(property.getName());
537   }
538
539   public final void removeModifiedProperty(final Property property) {
540     myModifiedPropertyNames.remove(property.getName());
541   }
542
543   public RadComponent getComponentToDrag(final Point pnt) {
544     return this;
545   }
546
547   public void processMouseEvent(final MouseEvent event) {
548   }
549
550   @Nullable
551   public EventProcessor getEventProcessor(final MouseEvent event) {
552     return null;
553   }
554
555   /**
556    * Serializes component into the passed <code>writer</code>
557    */
558   public abstract void write(XmlWriter writer);
559
560   /**
561    * Serializes component's ID
562    */
563   protected final void writeId(final XmlWriter writer) {
564     writer.addAttribute("id", getId());
565   }
566
567   /**
568    * Serializes component's class
569    */
570   protected final void writeClass(final XmlWriter writer) {
571     writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, getComponentClass().getName());
572   }
573
574   protected final void writeClassIfDifferent(final XmlWriter writer, String defaultClassName) {
575     if (!getComponentClassName().equals(defaultClassName)) {
576       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, getComponentClass().getName());
577     }
578   }
579
580   protected final void writeBinding(final XmlWriter writer) {
581     // Binding
582     if (getBinding() != null) {
583       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BINDING, getBinding());
584     }
585     if (isCustomCreate()) {
586       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CUSTOM_CREATE, Boolean.TRUE.toString());
587     }
588     if (isDefaultBinding()) {
589       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_DEFAULT_BINDING, Boolean.TRUE.toString());
590     }
591   }
592
593   protected void writeConstraints(final XmlWriter writer) {
594     writer.startElement("constraints");
595     try {
596       if (getParent() != null) {
597         getParent().getLayoutManager().writeChildConstraints(writer, this);
598       }
599     }
600     finally {
601       writer.endElement(); // constraints
602     }
603   }
604
605   protected final void writeProperties(final XmlWriter writer) {
606     writer.startElement(UIFormXmlConstants.ELEMENT_PROPERTIES);
607     try {
608       final IntrospectedProperty[] introspectedProperties =
609         getPalette().getIntrospectedProperties(this);
610       for (final IntrospectedProperty property : introspectedProperties) {
611         if (isMarkedAsModified(property)) {
612           final Object value = property.getValue(this);
613           if (value != null) {
614             writer.startElement(property.getName());
615             try {
616               //noinspection unchecked
617               property.write(value, writer);
618             }
619             finally {
620               writer.endElement();
621             }
622           }
623         }
624       }
625     }
626     finally {
627       writer.endElement(); // properties
628     }
629     writeClientProperties(writer);
630   }
631
632   private void writeClientProperties(final XmlWriter writer) {
633     if (myModule == null) return;
634     boolean haveClientProperties = false;
635     try {
636       ClientPropertiesProperty cpp = ClientPropertiesProperty.getInstance(getProject());
637       for (Property prop : cpp.getChildren(this)) {
638         ClientPropertyProperty clientProp = (ClientPropertyProperty)prop;
639         final Object value = getDelegee().getClientProperty(clientProp.getName());
640         if (value != null) {
641           if (!haveClientProperties) {
642             writer.startElement(UIFormXmlConstants.ELEMENT_CLIENT_PROPERTIES);
643             haveClientProperties = true;
644           }
645           writer.startElement(clientProp.getName());
646           writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, value.getClass().getName());
647           writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, value.toString());
648           writer.endElement();
649         }
650       }
651     }
652     finally {
653       if (haveClientProperties) {
654         writer.endElement();
655       }
656     }
657   }
658
659   public void fireConstraintsChanged(GridConstraints oldConstraints) {
660     firePropertyChanged(PROP_CONSTRAINTS, oldConstraints, myConstraints);
661   }
662
663   public IProperty[] getModifiedProperties() {
664     IntrospectedProperty[] props = getPalette().getIntrospectedProperties(this);
665     ArrayList<IProperty> result = new ArrayList<IProperty>();
666     for (IntrospectedProperty prop : props) {
667       if (isMarkedAsModified(prop)) {
668         result.add(prop);
669       }
670     }
671     return result.toArray(new IProperty[result.size()]);
672   }
673
674   public IContainer getParentContainer() {
675     return myParent;
676   }
677
678   public boolean hasIntrospectedProperties() {
679     return true;
680   }
681
682   public boolean accept(ComponentVisitor visitor) {
683     return visitor.visit(this);
684   }
685
686   public boolean areChildrenExclusive() {
687     return false;
688   }
689
690   public void loadLwProperty(final LwComponent lwComponent,
691                              final LwIntrospectedProperty lwProperty,
692                              final IntrospectedProperty property) {
693     myLoadingProperties = true;
694     try {
695       try {
696         final Object value = lwComponent.getPropertyValue(lwProperty);
697         //noinspection unchecked
698         property.setValue(this, value);
699       }
700       catch (Exception e) {
701         LOG.error(e);
702         //TODO[anton,vova]: show error and continue to load form
703       }
704     }
705     finally {
706       myLoadingProperties = false;
707     }
708   }
709
710   public void doneLoadingFromLw() {
711   }
712
713   @Nullable
714   public static RadComponent createSnapshotComponent(final SnapshotContext context, final JComponent component) {
715     String id = context.newId();
716     RadComponent result;
717
718     Class componentClass = component.getClass();
719     if (componentClass.isAnonymousClass()) {
720       componentClass = componentClass.getSuperclass();
721     }
722     if (component instanceof JPanel && !isCompositeComponent(component)) {
723       RadContainer container = new RadContainer(componentClass, id, context.getPalette());
724       final RadLayoutManager manager = LayoutManagerRegistry.createFromLayout(component.getLayout());
725       if (manager == null) {
726         return null;
727       }
728       container.setLayoutManager(manager);
729       result = container;
730     }
731     else if (component instanceof Box.Filler) {
732       Box.Filler filler = (Box.Filler)component;
733       if (filler.getMaximumSize().height == Short.MAX_VALUE) {
734         result = new RadVSpacer(null, id);
735         result.getConstraints().setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
736       }
737       else {
738         result = new RadHSpacer(null, id);
739         result.getConstraints().setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
740       }
741     }
742     else {
743       final RadComponentFactory factory = InsertComponentProcessor.getRadComponentFactory(componentClass);
744       if (factory == null) {
745         result = new RadAtomicComponent(componentClass, id, context.getPalette());
746       }
747       else {
748         result = factory.newInstance(componentClass, id, context.getPalette());
749       }
750     }
751
752     context.registerComponent(component, result);
753     result.importSnapshotComponent(context, component);
754
755     final IntrospectedProperty[] properties = context.getPalette().getIntrospectedProperties(component.getClass(),
756                                                                                              result.getDelegee().getClass());
757     for (IntrospectedProperty prop : properties) {
758       if (component instanceof AbstractButton) {
759         AbstractButton btn = (AbstractButton)component;
760         if (prop.getName().equals(SwingProperties.LABEL) && btn.getLabel().equals(btn.getText())) {
761           continue;
762         }
763         if (prop.getName().equals(SwingProperties.ACTION_COMMAND) && btn.getActionCommand().equals(btn.getText())) {
764           continue;
765         }
766       }
767       prop.importSnapshotValue(context, component, result);
768     }
769
770     if (component instanceof AbstractButton) {
771       AbstractButton btn = (AbstractButton)component;
772       if (btn.getModel() instanceof DefaultButtonModel) {
773         DefaultButtonModel model = (DefaultButtonModel)btn.getModel();
774         if (model.getGroup() != null) {
775           context.registerButtonGroup(model.getGroup());
776         }
777       }
778     }
779
780     return result;
781   }
782
783   private static boolean isCompositeComponent(final JComponent component) {
784     if (component.getComponentCount() == 0) {
785       return false;
786     }
787
788     JComponent instance;
789     try {
790       instance = component.getClass().newInstance();
791     }
792     catch (Exception ex) {
793       return false;
794     }
795     return instance.getComponentCount() == component.getComponentCount();
796   }
797
798   protected void importSnapshotComponent(final SnapshotContext context, final JComponent component) {
799   }
800
801   @Nullable
802   public String getComponentTitle() {
803     Palette palette = Palette.getInstance(getModule().getProject());
804     IntrospectedProperty[] props = palette.getIntrospectedProperties(this);
805     for (IntrospectedProperty prop : props) {
806       if (prop.getName().equals(SwingProperties.TEXT) && prop instanceof IntroStringProperty) {
807         StringDescriptor value = (StringDescriptor)prop.getValue(this);
808         if (value != null) {
809           return "\"" + value.getResolvedValue() + "\"";
810         }
811       }
812     }
813
814     if (this instanceof RadContainer) {
815       RadContainer container = (RadContainer)this;
816       StringDescriptor descriptor = container.getBorderTitle();
817       if (descriptor != null) {
818         if (descriptor.getResolvedValue() == null) {
819           descriptor.setResolvedValue(StringDescriptorManager.getInstance(getModule()).resolve(this, descriptor));
820         }
821         return "\"" + descriptor.getResolvedValue() + "\"";
822       }
823     }
824
825     if (getParent() instanceof RadTabbedPane) {
826       RadTabbedPane parentTabbedPane = (RadTabbedPane)getParent();
827       final StringDescriptor descriptor = parentTabbedPane.getChildTitle(this);
828       if (descriptor != null) {
829         if (descriptor.getResolvedValue() == null) {
830           descriptor.setResolvedValue(StringDescriptorManager.getInstance(getModule()).resolve(this, descriptor));
831         }
832         return "\"" + descriptor.getResolvedValue() + "\"";
833       }
834       else {
835         parentTabbedPane.getChildTitle(this);
836       }
837     }
838     return null;
839   }
840
841   public String getDisplayName() {
842     StringBuilder titleBuilder = new StringBuilder();
843     if (getBinding() != null) {
844       titleBuilder.append(getBinding());
845     }
846     else {
847       final String className = getComponentClassName();
848       int pos = className.lastIndexOf('.');
849       if (pos < 0) {
850         titleBuilder.append(className);
851       }
852       else {
853         titleBuilder.append(className.substring(pos + 1).replace('$', '.'));
854       }
855       final String title = getComponentTitle();
856       if (title != null) {
857         titleBuilder.append(" ").append(title);
858       }
859     }
860     return titleBuilder.toString();
861   }
862 }