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