update copyright
[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 public final Project getProject() {
192     return myModule.getProject();
193   }
194
195   public Palette getPalette() {
196     if (myPalette == null) {
197       return Palette.getInstance(getProject());
198     }
199     return myPalette;
200   }
201
202   public void setPalette(final Palette palette) {
203     myPalette = palette;
204   }
205
206   /**
207    * Initializes introspected properties into default values and
208    * sets default component's constraints.
209    */
210   public void init(final GuiEditor editor, @NotNull final ComponentItem item) {
211     initDefaultProperties(item);
212   }
213
214   public void initDefaultProperties(@NotNull final ComponentItem item) {
215     final IntrospectedProperty[] properties = getPalette().getIntrospectedProperties(this);
216     for (final IntrospectedProperty property : properties) {
217       final Object initialValue = item.getInitialValue(property);
218       if (initialValue != null) {
219         try {
220           //noinspection unchecked
221           property.setValue(this, initialValue);
222         }
223         catch (Exception e) {
224           throw new RuntimeException(e);
225         }
226       }
227     }
228
229     myConstraints.restore(item.getDefaultConstraints());
230   }
231
232   /**
233    * @return the component's id. It is unique within the form.
234    */
235   @NotNull
236   public final String getId(){
237     return myId;
238   }
239
240   public final String getBinding(){
241     return myBinding;
242   }
243
244   public final void setBinding(final String binding){
245     //TODO[anton,vova]: check that binding is a valid java identifier!!!
246     myBinding = binding;
247   }
248
249   public boolean isCustomCreate() {
250     return myCustomCreate;
251   }
252
253   public void setCustomCreate(final boolean customCreate) {
254     myCustomCreate = customCreate;
255   }
256
257   public boolean isCustomCreateRequired() {
258     return !getDelegee().getClass().equals(getComponentClass());
259   }
260
261   /**
262    * @return Swing delegee component. The <code>RadComponent</code> has the same
263    * delegee during all its life.
264    */
265   @NotNull
266   public final JComponent getDelegee(){
267     return myDelegee;
268   }
269
270   /**
271    * Sometime bounds of the inplace editor depends on the point where
272    * user invoked inplace editor.
273    *
274    * @param x x in delegee coordinate system
275    * @param y y in delegee coordinate system
276    *
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    *
304    * @return area where editor component is located. This is the hint to the
305    * designer.  Designer can use or not this rectangle.
306    */
307   @Nullable
308   public Rectangle getInplaceEditorBounds(@NotNull final Property property, final int x, final int y){
309     return null;
310   }
311
312   @NotNull
313   public final Class getComponentClass(){
314     return myClass;
315   }
316
317   @NotNull
318   public String getComponentClassName() {
319     return myClass.getName();
320   }
321
322   public final Object getCustomLayoutConstraints(){
323     return myCustomLayoutConstraints;
324   }
325
326   public final void setCustomLayoutConstraints(final Object customConstraints){
327     myCustomLayoutConstraints = customConstraints;
328   }
329
330   public void changeCustomLayoutConstraints(final Object constraints) {
331     setCustomLayoutConstraints(constraints);
332     // update constraints in CardLayout
333     final JComponent parent = getParent().getDelegee();
334     for (int i = 0; i < parent.getComponentCount(); i++) {
335       if (parent.getComponent(i) == getDelegee()) {
336         parent.remove(i);
337         parent.add(getDelegee(), constraints, i);
338         break;
339       }
340     }
341   }
342
343   public final boolean hasDragger(){
344     return myHasDragger;
345   }
346
347   public final void setDragger(final boolean hasDragger){
348     myHasDragger = hasDragger;
349   }
350
351   public boolean isResizing() {
352     return myResizing;
353   }
354
355   public void setResizing(final boolean resizing) {
356     myResizing = resizing;
357   }
358
359   public boolean isDragging() {
360     return myDragging;
361   }
362
363   public void setDragging(final boolean dragging) {
364     myDragging = dragging;
365     RadContainer parent = getParent();
366     if (parent != null) {
367       parent.getLayoutManager().setChildDragging(this, dragging);
368     }
369   }
370
371   public void setDragBorder(final boolean dragging) {
372     myDragging = dragging;
373     myDragBorder = dragging;
374   }
375
376   public boolean isDragBorder() {
377     return myDragBorder;
378   }
379
380   public boolean isDefaultBinding() {
381     return myDefaultBinding;
382   }
383
384   public void setDefaultBinding(final boolean defaultBinding) {
385     myDefaultBinding = defaultBinding;
386   }
387
388   public final void addPropertyChangeListener(final PropertyChangeListener l){
389     final PropertyChangeListener[] propertyChangeListeners = myChangeSupport.getPropertyChangeListeners();
390     for(PropertyChangeListener listener: propertyChangeListeners) {
391       assert listener != l;
392     }
393     myChangeSupport.addPropertyChangeListener(l);
394   }
395
396   public final void removePropertyChangeListener(final PropertyChangeListener l){
397     myChangeSupport.removePropertyChangeListener(l);
398   }
399
400   protected final void firePropertyChanged(
401     @NotNull final String propertyName,
402     final Object oldValue,
403     final Object newValue
404   ){
405     myChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
406   }
407
408   /**
409    * @return component's constarints.
410    */
411   @NotNull
412   public final GridConstraints getConstraints(){
413     return myConstraints;
414   }
415
416   public final RadContainer getParent(){
417     return myParent;
418   }
419
420   public final void setParent(final RadContainer parent){
421     myParent = parent;
422   }
423
424   public boolean isSelected(){
425     return mySelected;
426   }
427
428   public void setSelected(final boolean selected){
429     if (mySelected != selected) {
430       mySelected = selected;
431       firePropertyChanged(PROP_SELECTED, !mySelected, mySelected);
432       GuiEditor.repaintLayeredPane(this);
433     }
434   }
435
436   /**
437    * @see JComponent#getClientProperty(Object)
438    */
439   public final Object getClientProperty(@NotNull final Object key){
440     return myDelegee.getClientProperty(key);
441   }
442
443   /**
444    * @see JComponent#putClientProperty(Object, Object)
445    */
446   public final void putClientProperty(@NotNull final Object key, final Object value){
447     myDelegee.putClientProperty(key, value);
448   }
449
450   public final int getX() {
451     return myDelegee.getX();
452   }
453
454   public final int getY() {
455     return myDelegee.getY();
456   }
457
458   public final void setLocation(final Point location){
459     myDelegee.setLocation(location);
460   }
461
462   public final void shift(final int dx, final int dy){
463     myDelegee.setLocation(myDelegee.getX() + dx, myDelegee.getY() + dy);
464   }
465
466   public final int getWidth(){
467     return myDelegee.getWidth();
468   }
469
470   public final int getHeight(){
471     return myDelegee.getHeight();
472   }
473
474   public final Dimension getSize() {
475     return myDelegee.getSize();
476   }
477
478   public final void setSize(final Dimension size) {
479     myDelegee.setSize(size);
480   }
481
482   /**
483    * @return bounds of the delegee in the parent container
484    */
485   public final Rectangle getBounds() {
486     return myDelegee.getBounds();
487   }
488
489   public final void setBounds(final Rectangle bounds) {
490     myDelegee.setBounds(bounds);
491   }
492
493   public final Dimension getMinimumSize(){
494     return Util.getMinimumSize(myDelegee, myConstraints, false);
495   }
496
497   public final Dimension getPreferredSize(){
498     return Util.getPreferredSize(myDelegee, myConstraints, false);
499   }
500
501   public final void revalidate() {
502     RadContainer theContainer = null;
503
504     for (RadContainer container = this instanceof RadContainer ? (RadContainer)this : getParent(); container != null; container = container.getParent()) {
505       final RadContainer parent = container.getParent();
506       if (parent != null && parent.isXY()) {
507         final Dimension size = container.getSize();
508         final Dimension minimumSize = container.getMinimumSize();
509         if (size.width < minimumSize.width || size.height < minimumSize.height) {
510           theContainer = container;
511         }
512       }
513     }
514
515     if (theContainer != null) {
516       final Dimension minimumSize = theContainer.getMinimumSize();
517
518       minimumSize.width = Math.max(minimumSize.width, theContainer.getWidth());
519       minimumSize.height = Math.max(minimumSize.height, theContainer.getHeight());
520
521       theContainer.getDelegee().setSize(minimumSize);
522     }
523
524     myDelegee.revalidate();
525   }
526
527   public final boolean isMarkedAsModified(final Property property) {
528     return myModifiedPropertyNames.contains(property.getName());
529   }
530
531   public final void markPropertyAsModified(final Property property) {
532     myModifiedPropertyNames.add(property.getName());
533   }
534
535   public final void removeModifiedProperty(final Property property) {
536     myModifiedPropertyNames.remove(property.getName());
537   }
538
539   public RadComponent getComponentToDrag(final Point pnt) {
540     return this;
541   }
542
543   public void processMouseEvent(final MouseEvent event) {}
544
545   @Nullable
546   public EventProcessor getEventProcessor(final MouseEvent event) {
547     return null;
548   }
549
550   /**
551    * Serializes component into the passed <code>writer</code>
552    */
553   public abstract void write(XmlWriter writer);
554
555   /**
556    * Serializes component's ID
557    */
558   protected final void writeId(final XmlWriter writer){
559     writer.addAttribute("id", getId());
560   }
561
562   /**
563    * Serializes component's class
564    */
565   protected final void writeClass(final XmlWriter writer){
566     writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, getComponentClass().getName());
567   }
568
569   protected final void writeClassIfDifferent(final XmlWriter writer, String defaultClassName) {
570     if (!getComponentClassName().equals(defaultClassName)) {
571       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, getComponentClass().getName());
572     }
573   }
574
575   protected final void writeBinding(final XmlWriter writer){
576     // Binding
577     if (getBinding() != null){
578       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BINDING, getBinding());
579     }
580     if (isCustomCreate()) {
581       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CUSTOM_CREATE, Boolean.TRUE.toString());
582     }
583     if (isDefaultBinding()) {
584       writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_DEFAULT_BINDING, Boolean.TRUE.toString());
585     }
586   }
587
588   protected void writeConstraints(final XmlWriter writer){
589     writer.startElement("constraints");
590     try {
591       if (getParent() != null) {
592         getParent().getLayoutManager().writeChildConstraints(writer, this);
593       }
594     } finally {
595       writer.endElement(); // constraints
596     }
597   }
598
599   protected final void writeProperties(final XmlWriter writer){
600     writer.startElement(UIFormXmlConstants.ELEMENT_PROPERTIES);
601     try{
602       final IntrospectedProperty[] introspectedProperties =
603         getPalette().getIntrospectedProperties(this);
604       for(final IntrospectedProperty property : introspectedProperties) {
605         if (isMarkedAsModified(property)) {
606           final Object value = property.getValue(this);
607           if (value != null) {
608             writer.startElement(property.getName());
609             try {
610               //noinspection unchecked
611               property.write(value, writer);
612             }
613             finally {
614               writer.endElement();
615             }
616           }
617         }
618       }
619     }finally{
620       writer.endElement(); // properties
621     }
622     writeClientProperties(writer);
623   }
624
625   private void writeClientProperties(final XmlWriter writer) {
626     if (myModule == null) return;
627     boolean haveClientProperties = false;
628     try {
629       ClientPropertiesProperty cpp = ClientPropertiesProperty.getInstance(getProject());
630       for(Property prop: cpp.getChildren(this)) {
631         ClientPropertyProperty clientProp = (ClientPropertyProperty) prop;
632         final Object value = getDelegee().getClientProperty(clientProp.getName());
633         if (value != null) {
634           if (!haveClientProperties) {
635             writer.startElement(UIFormXmlConstants.ELEMENT_CLIENT_PROPERTIES);
636             haveClientProperties = true;
637           }
638           writer.startElement(clientProp.getName());
639           writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, value.getClass().getName());
640           writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, value.toString());
641           writer.endElement();
642         }
643       }
644     }
645     finally {
646       if (haveClientProperties) {
647         writer.endElement();
648       }
649     }
650   }
651
652   public void fireConstraintsChanged(GridConstraints oldConstraints) {
653     firePropertyChanged(PROP_CONSTRAINTS, oldConstraints, myConstraints);
654   }
655
656   public IProperty[] getModifiedProperties() {
657     IntrospectedProperty[] props = getPalette().getIntrospectedProperties(this);
658     ArrayList<IProperty> result = new ArrayList<IProperty>();
659     for(IntrospectedProperty prop: props) {
660       if (isMarkedAsModified(prop)) {
661         result.add(prop);
662       }
663     }
664     return result.toArray(new IProperty[result.size()]);
665   }
666
667   public IContainer getParentContainer() {
668     return myParent;
669   }
670
671   public boolean hasIntrospectedProperties() {
672     return true;
673   }
674
675   public boolean accept(ComponentVisitor visitor) {
676     return visitor.visit(this);
677   }
678
679   public boolean areChildrenExclusive() {
680     return false;
681   }
682
683   public void loadLwProperty(final LwComponent lwComponent,
684                              final LwIntrospectedProperty lwProperty,
685                              final IntrospectedProperty property) {
686     myLoadingProperties = true;
687     try {
688       try {
689         final Object value = lwComponent.getPropertyValue(lwProperty);
690         //noinspection unchecked
691         property.setValue(this, value);
692       }
693       catch (Exception e) {
694         LOG.error(e);
695         //TODO[anton,vova]: show error and continue to load form
696       }
697     }
698     finally {
699       myLoadingProperties = false;
700     }
701   }
702
703   public void doneLoadingFromLw() {
704   }
705
706   @Nullable
707   public static RadComponent createSnapshotComponent(final SnapshotContext context, final JComponent component) {
708     String id = context.newId();
709     RadComponent result;
710
711     Class componentClass = component.getClass();
712     if (componentClass.isAnonymousClass()) {
713       componentClass = componentClass.getSuperclass();
714     }
715     if (component instanceof JPanel && !isCompositeComponent(component)) {
716       RadContainer container = new RadContainer(componentClass, id, context.getPalette());
717       final RadLayoutManager manager = LayoutManagerRegistry.createFromLayout(component.getLayout());
718       if (manager == null) {
719         return null;
720       }
721       container.setLayoutManager(manager);
722       result = container;
723     }
724     else if (component instanceof Box.Filler) {
725       Box.Filler filler = (Box.Filler) component;
726       if (filler.getMaximumSize().height == Short.MAX_VALUE) {
727         result = new RadVSpacer(null, id);
728         result.getConstraints().setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
729       }
730       else {
731         result = new RadHSpacer(null, id);
732         result.getConstraints().setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
733       }
734     }
735     else {
736       final RadComponentFactory factory = InsertComponentProcessor.getRadComponentFactory(componentClass);
737       if (factory == null) {
738         result = new RadAtomicComponent(componentClass, id, context.getPalette());
739       }
740       else {
741         result = factory.newInstance(componentClass, id, context.getPalette());
742       }
743     }
744
745     context.registerComponent(component, result);
746     result.importSnapshotComponent(context, component);
747
748     final IntrospectedProperty[] properties = context.getPalette().getIntrospectedProperties(component.getClass(),
749                                                                                              result.getDelegee().getClass());
750     for(IntrospectedProperty prop: properties) {
751       if (component instanceof AbstractButton) {
752         AbstractButton btn = (AbstractButton) component;
753         if (prop.getName().equals(SwingProperties.LABEL) && btn.getLabel().equals(btn.getText())) {
754           continue;
755         }
756         if (prop.getName().equals(SwingProperties.ACTION_COMMAND) && btn.getActionCommand().equals(btn.getText())) {
757           continue;
758         }
759       }
760       prop.importSnapshotValue(context, component, result);
761
762     }
763
764     if (component instanceof AbstractButton) {
765       AbstractButton btn = (AbstractButton) component;
766       if (btn.getModel() instanceof DefaultButtonModel) {
767         DefaultButtonModel model = (DefaultButtonModel) btn.getModel();
768         if (model.getGroup() != null) {
769           context.registerButtonGroup(model.getGroup());
770         }
771       }
772     }
773
774     return result;
775   }
776
777   private static boolean isCompositeComponent(final JComponent component) {
778     if (component.getComponentCount() == 0) {
779       return false;
780     }
781     
782     JComponent instance;
783     try {
784       instance = component.getClass().newInstance();
785     }
786     catch(Exception ex) {
787       return false;
788     }
789     return instance.getComponentCount() == component.getComponentCount();
790   }
791
792   protected void importSnapshotComponent(final SnapshotContext context, final JComponent component) {
793   }
794
795   @Nullable
796   public String getComponentTitle() {
797     Palette palette = Palette.getInstance(getModule().getProject());
798     IntrospectedProperty[] props = palette.getIntrospectedProperties(this);
799     for(IntrospectedProperty prop: props) {
800       if (prop.getName().equals(SwingProperties.TEXT) && prop instanceof IntroStringProperty) {
801         StringDescriptor value = (StringDescriptor) prop.getValue(this);
802         if (value != null) {
803           return "\"" + value.getResolvedValue() + "\"";
804         }
805       }
806     }
807
808     if (this instanceof RadContainer) {
809       RadContainer container = (RadContainer)this;
810       StringDescriptor descriptor = container.getBorderTitle();
811       if (descriptor != null) {
812         if (descriptor.getResolvedValue() == null) {
813           descriptor.setResolvedValue(StringDescriptorManager.getInstance(getModule()).resolve(this, descriptor));
814         }
815         return "\"" + descriptor.getResolvedValue() + "\"";
816       }
817     }
818
819     if (getParent() instanceof RadTabbedPane) {
820       RadTabbedPane parentTabbedPane = (RadTabbedPane) getParent();
821       final StringDescriptor descriptor = parentTabbedPane.getChildTitle(this);
822       if (descriptor != null) {
823         if (descriptor.getResolvedValue() == null) {
824           descriptor.setResolvedValue(StringDescriptorManager.getInstance(getModule()).resolve(this, descriptor));
825         }
826         return "\"" + descriptor.getResolvedValue() + "\"";
827       }
828       else {
829         parentTabbedPane.getChildTitle(this);
830       }
831     }
832     return null;
833   }
834
835   public String getDisplayName() {
836     StringBuilder titleBuilder = new StringBuilder();
837     if (getBinding() != null) {
838       titleBuilder.append(getBinding());
839     }
840     else {
841       final String className = getComponentClassName();
842       int pos = className.lastIndexOf('.');
843       if (pos < 0) {
844         titleBuilder.append(className);
845       }
846       else {
847         titleBuilder.append(className.substring(pos + 1).replace('$', '.'));
848       }
849       final String title = getComponentTitle();
850       if (title != null) {
851         titleBuilder.append(" ").append(title);
852       }
853     }
854     return titleBuilder.toString();
855   }
856 }