1/*******************************************************************************
2 * Copyright (c) 2011 Google, Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Google, Inc. - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.wb.internal.core.model.property.editor.presentation;
12
13import com.google.common.collect.Maps;
14
15import org.eclipse.swt.SWT;
16import org.eclipse.swt.graphics.Rectangle;
17import org.eclipse.swt.widgets.Button;
18import org.eclipse.swt.widgets.Control;
19import org.eclipse.swt.widgets.Event;
20import org.eclipse.swt.widgets.Listener;
21import org.eclipse.wb.internal.core.model.property.Property;
22import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
23import org.eclipse.wb.internal.core.utils.Pair;
24
25import java.util.Map;
26
27/**
28 * Internal implementation of {@link PropertyEditorPresentation} for displaying {@link Button}.
29 *
30 * @author scheglov_ke
31 * @author mitin_aa
32 * @coverage core.model.property.editor
33 */
34class ButtonPropertyEditorPresentationImpl extends PropertyEditorPresentation {
35  protected final PropertyToControlMap m_propertyToControl = new PropertyToControlMap();
36  private final ButtonPropertyEditorPresentation m_presentation;
37
38  ////////////////////////////////////////////////////////////////////////////
39  //
40  // Constructor
41  //
42  ////////////////////////////////////////////////////////////////////////////
43  public ButtonPropertyEditorPresentationImpl(ButtonPropertyEditorPresentation presentation) {
44    m_presentation = presentation;
45  }
46
47  ////////////////////////////////////////////////////////////////////////////
48  //
49  // PropertyEditorPresentation
50  //
51  ////////////////////////////////////////////////////////////////////////////
52  @Override
53  public final void hide(PropertyTable propertyTable, Property property) {
54    Control control = m_propertyToControl.remove(propertyTable, property);
55    if (control != null) {
56      control.dispose();
57    }
58  }
59
60  @Override
61  public final int show(PropertyTable propertyTable,
62      Property property,
63      int x,
64      int y,
65      int width,
66      int height) {
67    // prepare control
68    Control control = m_propertyToControl.get(propertyTable, property);
69    if (control == null) {
70      control = createControl(propertyTable, property);
71    }
72    // set bounds
73    final int controlWidth = height;
74    final int controlX = x + width - controlWidth;
75    setBounds(control, controlX, y, controlWidth, height);
76    return controlWidth;
77  }
78
79  /**
80   * Finds and select the appropriate {@link Control} belonging to given property.
81   */
82  public void setSelection(PropertyTable propertyTable, Property property, boolean selected) {
83    Button button = (Button) m_propertyToControl.get(propertyTable, property);
84    if (button != null) {
85      button.setSelection(selected);
86    }
87  }
88
89  ////////////////////////////////////////////////////////////////////////////
90  //
91  // Control
92  //
93  ////////////////////////////////////////////////////////////////////////////
94  /**
95   * Creates the control for given property and initializes newly created control.
96   */
97  private Control createControl(final PropertyTable propertyTable, final Property property) {
98    Control control = createControlImpl(propertyTable, property);
99    m_propertyToControl.put(propertyTable, property, control);
100    // when Control disposed, remove Control/Property from map to avoid memory leak
101    control.addListener(SWT.Dispose, new Listener() {
102      @Override
103    public void handleEvent(Event e) {
104        m_propertyToControl.remove(propertyTable, property);
105      }
106    });
107    // activate property on mouse down
108    control.addListener(SWT.MouseDown, new Listener() {
109      @Override
110    public void handleEvent(Event event) {
111        propertyTable.deactivateEditor(true);
112        propertyTable.setActiveProperty(property);
113      }
114    });
115    // return focus on propertyTable after click
116    control.addListener(SWT.MouseUp, new Listener() {
117      @Override
118    public void handleEvent(Event event) {
119        propertyTable.forceFocus();
120      }
121    });
122    // handle selection
123    control.addListener(SWT.Selection, new Listener() {
124      @Override
125    public void handleEvent(Event event) {
126        try {
127          getPresentation().onClick(propertyTable, property);
128        } catch (Throwable e) {
129          propertyTable.deactivateEditor(false);
130          propertyTable.handleException(e);
131        }
132      }
133    });
134    return control;
135  }
136
137  /**
138   * Creates the {@link Control} instance. By default, {@link Button} instance created.
139   */
140  protected Control createControlImpl(final PropertyTable propertyTable, final Property property) {
141    Button button = new Button(propertyTable, getPresentation().getStyle());
142    button.setImage(getPresentation().getImage());
143    button.setToolTipText(getPresentation().getTooltip());
144    return button;
145  }
146
147  ////////////////////////////////////////////////////////////////////////////
148  //
149  // Access
150  //
151  ////////////////////////////////////////////////////////////////////////////
152  /**
153   * @return the 'parent' presentation. Internal usage only.
154   */
155  protected final ButtonPropertyEditorPresentation getPresentation() {
156    return m_presentation;
157  }
158
159  ////////////////////////////////////////////////////////////////////////////
160  //
161  // Utils
162  //
163  ////////////////////////////////////////////////////////////////////////////
164  /**
165   * Sets new bounds for {@link Control}, optimizing when possible.
166   */
167  private static void setBounds(Control control, int newX, int newY, int newWidth, int newHeight) {
168    // check, may be Control is invisible, so no reason to change bounds
169    {
170      // is in negative zone
171      if (newY + newHeight < 0) {
172        control.setVisible(false);
173        return;
174      }
175      // is out of client area height
176      Rectangle parentArea = control.getParent().getClientArea();
177      if (newY > parentArea.height) {
178        control.setVisible(false);
179        return;
180      }
181    }
182    // well, now we sure that Control is visible
183    if (!control.getVisible()) {
184      control.setVisible(true);
185    }
186    // prepare old size, remember new
187    Integer oldWidthObject = (Integer) control.getData("oldWidth");
188    Integer oldHeightObject = (Integer) control.getData("oldHeight");
189    control.setData("oldWidth", newWidth);
190    control.setData("oldHeight", newHeight);
191    // check, may be same size
192    if (oldWidthObject != null) {
193      int oldWidth = oldWidthObject.intValue();
194      int oldHeight = oldHeightObject.intValue();
195      if (oldWidth == newWidth && oldHeight == newHeight) {
196        control.setLocation(newX, newY);
197        return;
198      }
199    }
200    // no any optimization possible, just set bounds
201    control.setBounds(newX, newY, newWidth, newHeight);
202  }
203
204  ////////////////////////////////////////////////////////////////////////////
205  //
206  // Controls map
207  //
208  ////////////////////////////////////////////////////////////////////////////
209  protected static final class PropertyToControlMap {
210    private final Map<Pair<PropertyTable, Property>, Control> m_map = Maps.newHashMap();
211
212    void put(PropertyTable propertyTable, Property property, Control control) {
213      m_map.put(Pair.create(propertyTable, property), control);
214    }
215
216    Control remove(PropertyTable propertyTable, Property property) {
217      return m_map.remove(Pair.create(propertyTable, property));
218    }
219
220    Control get(PropertyTable propertyTable, Property property) {
221      return m_map.get(Pair.create(propertyTable, property));
222    }
223  }
224}
225