editing for additional border properties: title justification, position, font and...
[fedora-idea.git] / ui-designer / impl / com / intellij / uiDesigner / radComponents / RadContainer.java
blobc2a3dd89d7ccd25eef6c9109cc4c15a9c5c5f850
1 package com.intellij.uiDesigner.radComponents;
3 import com.intellij.openapi.diagnostic.Logger;
4 import com.intellij.openapi.module.Module;
5 import com.intellij.openapi.util.Comparing;
6 import com.intellij.uiDesigner.GuiDesignerConfiguration;
7 import com.intellij.uiDesigner.ReferenceUtil;
8 import com.intellij.uiDesigner.UIFormXmlConstants;
9 import com.intellij.uiDesigner.XmlWriter;
10 import com.intellij.uiDesigner.snapShooter.SnapshotContext;
11 import com.intellij.uiDesigner.palette.Palette;
12 import com.intellij.uiDesigner.core.AbstractLayout;
13 import com.intellij.uiDesigner.core.GridConstraints;
14 import com.intellij.uiDesigner.core.GridLayoutManager;
15 import com.intellij.uiDesigner.designSurface.ComponentDragObject;
16 import com.intellij.uiDesigner.designSurface.DropLocation;
17 import com.intellij.uiDesigner.lw.*;
18 import com.intellij.uiDesigner.propertyInspector.Property;
19 import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
20 import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
21 import com.intellij.uiDesigner.propertyInspector.editors.string.StringEditor;
22 import com.intellij.uiDesigner.shared.BorderType;
23 import com.intellij.uiDesigner.shared.XYLayoutManager;
24 import org.jetbrains.annotations.NonNls;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
28 import javax.swing.*;
29 import javax.swing.border.Border;
30 import javax.swing.border.TitledBorder;
31 import javax.swing.border.EtchedBorder;
32 import javax.swing.border.BevelBorder;
33 import java.awt.*;
34 import java.util.ArrayList;
36 /**
37 * @author Anton Katilin
38 * @author Vladimir Kondratyev
40 public class RadContainer extends RadComponent implements IContainer {
41 private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.radComponents.RadContainer");
43 /**
44 * value: RadComponent[]
46 @NonNls
47 public static final String PROP_CHILDREN = "children";
48 /**
49 * Children components
51 private final ArrayList<RadComponent> myComponents;
52 /**
53 * Describes border's type.
55 @NotNull private BorderType myBorderType;
56 /**
57 * Border's title. If border doesn't have any title then
58 * this member is <code>null</code>.
60 @Nullable private StringDescriptor myBorderTitle;
61 private int myBorderTitleJustification;
62 private int myBorderTitlePosition;
63 private FontDescriptor myBorderTitleFont;
64 private ColorDescriptor myBorderTitleColor;
66 protected RadLayoutManager myLayoutManager;
68 public RadContainer(final Module module, final String id){
69 this(module, JPanel.class, id);
72 public RadContainer(final Module module, final Class aClass, final String id){
73 super(module, aClass, id);
75 myComponents = new ArrayList<RadComponent>();
77 // By default container doesn't have any special border
78 setBorderType(BorderType.NONE);
80 myLayoutManager = createInitialLayoutManager();
81 if (myLayoutManager != null) {
82 final LayoutManager layoutManager = myLayoutManager.createLayout();
83 if (layoutManager != null) {
84 setLayout(layoutManager);
89 public RadContainer(@NotNull final Class aClass, @NotNull final String id, final Palette palette) {
90 this(null, aClass, id);
91 setPalette(palette);
94 @Nullable protected RadLayoutManager createInitialLayoutManager() {
95 String defaultLayoutManager = UIFormXmlConstants.LAYOUT_INTELLIJ;
96 if (getModule() != null) {
97 final GuiDesignerConfiguration configuration = GuiDesignerConfiguration.getInstance(getModule().getProject());
98 if (configuration.IRIDA_LAYOUT_MODE) {
99 return new RadXYLayoutManager();
101 defaultLayoutManager = configuration.DEFAULT_LAYOUT_MANAGER;
104 try {
105 return RadLayoutManager.createLayoutManager(defaultLayoutManager);
107 catch (Exception e) {
108 LOG.error(e);
109 return new RadGridLayoutManager();
113 public Property getInplaceProperty(final int x, final int y) {
114 // 1. We have to check whether user clicked inside border (if any) or not.
115 // In this case we have return inplace editor for border text
116 final Insets insets = getDelegee().getInsets(); // border insets
118 x < insets.left || x > getWidth() - insets.right ||
119 y < 0 || y > insets.top
121 return super.getInplaceProperty(x, y);
124 // 2. Now we are sure that user clicked inside title area
125 return new MyBorderTitleProperty();
128 @Override @Nullable
129 public Property getDefaultInplaceProperty() {
130 return new MyBorderTitleProperty();
133 @Override @Nullable
134 public Rectangle getDefaultInplaceEditorBounds() {
135 return getBorderInPlaceEditorBounds(new MyBorderTitleProperty());
138 public Rectangle getInplaceEditorBounds(final Property property, final int x, final int y) {
139 if(property instanceof MyBorderTitleProperty){ // If this is our property
140 return getBorderInPlaceEditorBounds(property);
142 return super.getInplaceEditorBounds(property, x, y);
145 private Rectangle getBorderInPlaceEditorBounds(final Property property) {
146 final MyBorderTitleProperty _property = (MyBorderTitleProperty)property;
147 final Insets insets = getDelegee().getInsets();
148 return new Rectangle(
149 insets.left,
151 getWidth() - insets.left - insets.right,
152 _property.getPreferredSize().height
156 public final LayoutManager getLayout(){
157 return getDelegee().getLayout();
160 public final void setLayout(final LayoutManager layout) {
161 getDelegee().setLayout(layout);
163 if (layout instanceof AbstractLayout) {
164 AbstractLayout aLayout = (AbstractLayout) layout;
165 for (int i=0; i < getComponentCount(); i++) {
166 final RadComponent c = getComponent(i);
167 aLayout.addLayoutComponent(c.getDelegee(), c.getConstraints());
172 public final boolean isGrid(){
173 return getLayout() instanceof GridLayoutManager;
176 public final boolean isXY(){
177 return getLayout() instanceof XYLayoutManager;
181 * @param component component to be added.
183 * @exception java.lang.IllegalArgumentException if <code>component</code> is <code>null</code>
184 * @exception java.lang.IllegalArgumentException if <code>component</code> already exist in the
185 * container
187 public final void addComponent(@NotNull final RadComponent component, int index) {
188 if (myComponents.contains(component)) {
189 //noinspection HardCodedStringLiteral
190 throw new IllegalArgumentException("component is already added: " + component);
193 final RadComponent[] oldChildren = myComponents.toArray(new RadComponent[myComponents.size()]);
195 // Remove from old parent
196 final RadContainer oldParent=component.getParent();
197 if(oldParent!=null) {
198 oldParent.removeComponent(component);
201 // Attach to new parent
202 myComponents.add(index, component);
203 component.setParent(this);
204 myLayoutManager.addComponentToContainer(this, component, index);
206 final RadComponent[] newChildren = myComponents.toArray(new RadComponent[myComponents.size()]);
207 firePropertyChanged(PROP_CHILDREN, oldChildren, newChildren);
210 public final void addComponent(@NotNull final RadComponent component) {
211 addComponent(component, myComponents.size());
215 * Removes specified <code>component</code> from the container.
216 * This method also removes component's delegee from the
217 * container's delegee. Client code is responsible for revalidation
218 * of invalid Swing hierarchy.
220 * @param component component to be removed.
222 * @exception java.lang.IllegalArgumentException if <code>component</code>
223 * is <code>null</code>
224 * @exception java.lang.IllegalArgumentException if <code>component</code>
225 * doesn't exist in the container
227 public final void removeComponent(@NotNull final RadComponent component){
228 if(!myComponents.contains(component)){
229 //noinspection HardCodedStringLiteral
230 throw new IllegalArgumentException("component is not added: " + component);
233 final RadComponent[] oldChildren = myComponents.toArray(new RadComponent[myComponents.size()]);
235 // Remove child
236 component.setParent(null);
237 myComponents.remove(component);
238 myLayoutManager.removeComponentFromContainer(this, component);
240 final RadComponent[] newChildren = myComponents.toArray(new RadComponent[myComponents.size()]);
241 firePropertyChanged(PROP_CHILDREN, oldChildren, newChildren);
244 public final RadComponent getComponent(final int index) {
245 return myComponents.get(index);
248 public final int getComponentCount() {
249 return myComponents.size();
252 public int indexOfComponent(RadComponent component) {
253 return myComponents.indexOf(component);
257 * @return new array with all children
259 public final RadComponent[] getComponents() {
260 return myComponents.toArray(new RadComponent[myComponents.size()]);
263 @NotNull
264 public DropLocation getDropLocation(@Nullable Point location) {
265 return getLayoutManager().getDropLocation(this, location);
268 public RadComponent findComponentInRect(final int startRow, final int startCol, final int rowSpan, final int colSpan) {
269 for(int r=startRow; r < startRow + rowSpan; r++) {
270 for(int c=startCol; c < startCol + colSpan; c++) {
271 final RadComponent result = getComponentAtGrid(r, c);
272 if (result != null) {
273 return result;
277 return null;
280 @Nullable
281 public RadComponent getComponentAtGrid(boolean rowFirst, int coord1, int coord2) {
282 return rowFirst ? getComponentAtGrid(coord1, coord2) : getComponentAtGrid(coord2, coord1);
285 @Nullable
286 public RadComponent getComponentAtGrid(final int row, final int column) {
287 // If the target cell is not empty does not allow drop.
288 for(int i=0; i<getComponentCount(); i++){
289 final RadComponent component = getComponent(i);
290 if (component.isDragging()) {
291 continue;
293 final GridConstraints constraints=component.getConstraints();
295 constraints.getRow() <= row && row < constraints.getRow()+constraints.getRowSpan() &&
296 constraints.getColumn() <= column && column < constraints.getColumn()+constraints.getColSpan()
298 return component;
301 return null;
304 public final void dropIntoGrid(final RadComponent[] components, int row, int column, final ComponentDragObject dragObject) {
305 final GridLayoutManager gridLayout = (GridLayoutManager)getLayout();
306 assert components.length > 0;
308 for(int i=0; i<components.length; i++) {
309 RadComponent c = components [i];
310 if (c instanceof RadContainer) {
311 final LayoutManager layout = ((RadContainer)c).getLayout();
312 if (layout instanceof XYLayoutManager) {
313 ((XYLayoutManager)layout).setPreferredSize(c.getSize());
317 int relativeCol = dragObject.getRelativeCol(i);
318 int relativeRow = dragObject.getRelativeRow(i);
319 LOG.debug("dropIntoGrid: relativeRow=" + relativeRow + ", relativeCol=" + relativeCol);
320 int colSpan = dragObject.getColSpan(i);
321 int rowSpan = dragObject.getRowSpan(i);
323 assert row + relativeRow >= 0;
324 assert column + relativeCol >= 0;
325 assert relativeRow + rowSpan <= gridLayout.getRowCount();
326 assert relativeCol + colSpan <= gridLayout.getColumnCount();
328 RadComponent old = findComponentInRect(row + relativeRow, column + relativeCol, rowSpan, colSpan);
329 if (old != null) {
330 LOG.assertTrue(false,
331 "Drop rectangle not empty: (" + (row + relativeRow) + ", " + (column + relativeCol)
332 + ", " + rowSpan + ", " + colSpan + "), component ID=" + old.getId());
335 final GridConstraints constraints = c.getConstraints();
336 constraints.setRow(row + relativeRow);
337 constraints.setColumn(column + relativeCol);
338 constraints.setRowSpan(rowSpan);
339 constraints.setColSpan(colSpan);
340 addComponent(c);
342 // Fill DropInfo
343 c.revalidate();
346 revalidate();
350 * @return border's type.
352 * @see com.intellij.uiDesigner.shared.BorderType
354 @NotNull
355 public final BorderType getBorderType(){
356 return myBorderType;
360 * @see com.intellij.uiDesigner.shared.BorderType
362 * @exception java.lang.IllegalArgumentException if <code>type</code>
363 * is <code>null</code>
365 public final void setBorderType(@NotNull final BorderType type){
366 if(myBorderType==type){
367 return;
369 myBorderType=type;
370 updateBorder();
374 * @return border's title. If the container doesn't have any title then the
375 * method returns <code>null</code>.
377 @Nullable
378 public final StringDescriptor getBorderTitle(){
379 return myBorderTitle;
383 * @param title new border's title. <code>null</code> means that
384 * the containr doesn't have have titled border.
386 public final void setBorderTitle(final StringDescriptor title){
387 if(Comparing.equal(title,myBorderTitle)){
388 return;
390 myBorderTitle = title;
391 updateBorder();
394 public int getBorderTitleJustification() {
395 return myBorderTitleJustification;
398 public void setBorderTitleJustification(final int borderTitleJustification) {
399 if (myBorderTitleJustification != borderTitleJustification) {
400 myBorderTitleJustification = borderTitleJustification;
401 updateBorder();
405 public int getBorderTitlePosition() {
406 return myBorderTitlePosition;
409 public void setBorderTitlePosition(final int borderTitlePosition) {
410 if (myBorderTitlePosition != borderTitlePosition) {
411 myBorderTitlePosition = borderTitlePosition;
412 updateBorder();
416 public FontDescriptor getBorderTitleFont() {
417 return myBorderTitleFont;
420 public void setBorderTitleFont(final FontDescriptor borderTitleFont) {
421 if (!Comparing.equal(myBorderTitleFont, borderTitleFont)) {
422 myBorderTitleFont = borderTitleFont;
423 updateBorder();
427 public ColorDescriptor getBorderTitleColor() {
428 return myBorderTitleColor;
431 public void setBorderTitleColor(final ColorDescriptor borderTitleColor) {
432 if (!Comparing.equal(myBorderTitleColor, borderTitleColor)) {
433 myBorderTitleColor = borderTitleColor;
434 updateBorder();
439 * Updates delegee's border
441 public void updateBorder() {
442 String title = null;
443 if (myBorderTitle != null) {
444 title = ReferenceUtil.resolve(this, myBorderTitle);
446 Font font = (myBorderTitleFont != null) ? myBorderTitleFont.getResolvedFont() : null;
447 Color color = (myBorderTitleColor != null) ? myBorderTitleColor.getResolvedColor() : null;
448 getDelegee().setBorder(myBorderType.createBorder(title, myBorderTitleJustification, myBorderTitlePosition,
449 font, color));
452 public RadLayoutManager getLayoutManager() {
453 RadContainer parent = this;
454 while(parent != null) {
455 if (parent.myLayoutManager != null) {
456 return parent.myLayoutManager;
458 parent = parent.getParent();
460 return null;
463 public void setLayoutManager(final RadLayoutManager layoutManager) {
464 myLayoutManager = layoutManager;
465 setLayout(myLayoutManager.createLayout());
468 public void setLayoutManager(RadLayoutManager layoutManager, LayoutManager layout) {
469 myLayoutManager = layoutManager;
470 setLayout(layout);
473 public RadComponent getComponentToResize(RadComponent child) {
474 return child;
478 * Serializes container's border
480 protected final void writeBorder(final XmlWriter writer){
481 writer.startElement(UIFormXmlConstants.ELEMENT_BORDER);
482 try{
483 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TYPE, getBorderType().getId());
484 if (getBorderTitle() != null) {
485 final StringDescriptor descriptor = getBorderTitle();
486 writer.writeStringDescriptor(descriptor, UIFormXmlConstants.ATTRIBUTE_TITLE,
487 UIFormXmlConstants.ATTRIBUTE_TITLE_RESOURCE_BUNDLE,
488 UIFormXmlConstants.ATTRIBUTE_TITLE_KEY);
490 if (myBorderTitleJustification != 0) {
491 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TITLE_JUSTIFICATION, myBorderTitleJustification);
493 if (myBorderTitlePosition != 0) {
494 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TITLE_POSITION, myBorderTitlePosition);
496 if (myBorderTitleFont != null) {
497 writer.startElement(UIFormXmlConstants.ELEMENT_FONT);
498 writer.writeFontDescriptor(myBorderTitleFont);
499 writer.endElement();
501 if (myBorderTitleColor != null) {
502 writer.startElement(UIFormXmlConstants.ELEMENT_COLOR);
503 writer.writeColorDescriptor(myBorderTitleColor);
504 writer.endElement();
506 }finally{
507 writer.endElement(); // border
512 * Serializes container's children
514 protected final void writeChildren(final XmlWriter writer){
515 // Children
516 writer.startElement("children");
517 try{
518 writeChildrenImpl(writer);
519 }finally{
520 writer.endElement(); // children
524 protected final void writeChildrenImpl(final XmlWriter writer){
525 for (int i=0; i < getComponentCount(); i++) {
526 getComponent(i).write(writer);
530 public void write(final XmlWriter writer) {
531 if (isXY()) {
532 writer.startElement("xy");
534 else {
535 writer.startElement("grid");
537 try{
538 writeId(writer);
539 writeBinding(writer);
541 if (myLayoutManager != null) {
542 writer.addAttribute("layout-manager", myLayoutManager.getName());
545 getLayoutManager().writeLayout(writer, this);
547 // Constraints and properties
548 writeConstraints(writer);
549 writeProperties(writer);
551 // Border
552 writeBorder(writer);
554 // Children
555 writeChildren(writer);
556 }finally{
557 writer.endElement(); // xy/grid
561 public boolean accept(ComponentVisitor visitor) {
562 if (!super.accept(visitor)) {
563 return false;
566 for (int i = 0; i < getComponentCount(); i++) {
567 final IComponent c = getComponent(i);
568 if (!c.accept(visitor)) {
569 return false;
573 return true;
576 protected void writeNoLayout(final XmlWriter writer) {
577 writeId(writer);
578 writeBinding(writer);
580 // Constraints and properties
581 writeConstraints(writer);
582 writeProperties(writer);
584 // Margin and border
585 writeBorder(writer);
586 writeChildren(writer);
589 @Override
590 protected void importSnapshotComponent(final SnapshotContext context, final JComponent component) {
591 getLayoutManager().createSnapshotLayout(context, component, this, component.getLayout());
592 importSnapshotBorder(component);
593 for(Component child: component.getComponents()) {
594 if (child instanceof JComponent) {
595 RadComponent childComponent = createSnapshotComponent(context, (JComponent) child);
596 if (childComponent != null) {
597 getLayoutManager().addSnapshotComponent(component, (JComponent) child, this, childComponent);
603 private void importSnapshotBorder(final JComponent component) {
604 Border border = component.getBorder();
605 if (border != null) {
606 if (border instanceof TitledBorder) {
607 TitledBorder titledBorder = (TitledBorder) border;
608 setBorderTitle(StringDescriptor.create(titledBorder.getTitle()));
609 setBorderTitleJustification(titledBorder.getTitleJustification());
610 setBorderTitlePosition(titledBorder.getTitlePosition());
611 setBorderTitleFont(new FontDescriptor(titledBorder.getTitleFont()));
612 setBorderTitleColor(new ColorDescriptor(titledBorder.getTitleColor()));
613 border = titledBorder.getBorder();
616 if (border instanceof EtchedBorder) {
617 setBorderType(BorderType.ETCHED);
619 else if (border instanceof BevelBorder) {
620 BevelBorder bevelBorder = (BevelBorder) border;
621 setBorderType(bevelBorder.getBevelType() == BevelBorder.RAISED ? BorderType.BEVEL_RAISED : BorderType.BEVEL_LOWERED);
626 private final class MyBorderTitleProperty extends Property<RadContainer, StringDescriptor> {
627 private final StringEditor myEditor;
629 public MyBorderTitleProperty() {
630 super(null, "Title");
631 myEditor = new StringEditor(getModule().getProject());
634 public Dimension getPreferredSize(){
635 return myEditor.getPreferredSize();
638 public StringDescriptor getValue(final RadContainer component) {
639 return myBorderTitle;
642 protected void setValueImpl(final RadContainer container, final StringDescriptor value) throws Exception {
643 setBorderTitle(value);
646 @NotNull
647 public PropertyRenderer<StringDescriptor> getRenderer() {
648 return null;
651 public PropertyEditor<StringDescriptor> getEditor() {
652 return myEditor;